diff --git a/.github/workflows/app-release.yml b/.github/workflows/app-release.yml index 6182f9cac..edf95ad4f 100644 --- a/.github/workflows/app-release.yml +++ b/.github/workflows/app-release.yml @@ -78,7 +78,7 @@ jobs: if: startsWith(matrix.platform, 'ubuntu') run: | sudo apt-get update - sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libssl-dev sqlite3 + sudo apt-get install -y libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev - name: Install frontend dependencies run: pnpm install diff --git a/apps/app-frontend/package.json b/apps/app-frontend/package.json index 150e97701..aa03f4402 100644 --- a/apps/app-frontend/package.json +++ b/apps/app-frontend/package.json @@ -21,14 +21,15 @@ "@vintl/vintl": "^4.4.1", "dayjs": "^1.11.10", "floating-vue": "^5.2.2", - "mixpanel-browser": "^2.49.0", "ofetch": "^1.3.4", "pinia": "^2.1.7", "vite-svg-loader": "^5.1.0", "vue": "^3.4.21", "vue-multiselect": "3.0.0", "vue-router": "4.3.0", - "vue-virtual-scroller": "v2.0.0-beta.8" + "vue-virtual-scroller": "v2.0.0-beta.8", + "posthog-js": "^1.158.2", + "@sentry/vue": "^8.27.0" }, "devDependencies": { "@tauri-apps/cli": "^2.0.0-rc", diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue index d057d1e30..6f6cd7803 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -17,16 +17,9 @@ import { command_listener, warning_listener } from '@/helpers/events.js' import { MinimizeIcon, MaximizeIcon } from '@/assets/icons' import { type } from '@tauri-apps/plugin-os' import { isDev, getOS } from '@/helpers/utils.js' -import { - mixpanel_track, - mixpanel_init, - mixpanel_opt_out_tracking, - mixpanel_is_loaded, -} from '@/helpers/mixpanel' -import { saveWindowState, StateFlags } from '@tauri-apps/plugin-window-state' +import { initAnalytics, debugAnalytics, optOutAnalytics, trackEvent } from '@/helpers/analytics' import { getCurrentWindow } from '@tauri-apps/api/window' import { getVersion } from '@tauri-apps/api/app' -import { TauriEvent } from '@tauri-apps/api/event' import URLConfirmModal from '@/components/ui/URLConfirmModal.vue' import { install_from_file } from './helpers/pack' import { useError } from '@/store/error.js' @@ -90,11 +83,12 @@ async function setupApp() { themeStore.collapsedNavigation = collapsed_navigation themeStore.advancedRendering = advanced_rendering - mixpanel_init('014c7d6a336d0efaefe3aca91063748d', { debug: dev, persistence: 'localStorage' }) + initAnalytics() if (!telemetry) { - mixpanel_opt_out_tracking() + optOutAnalytics() } - mixpanel_track('Launched', { version, dev, onboarded }) + if (dev) debugAnalytics() + trackEvent('Launched', { version, dev, onboarded }) if (!dev) document.addEventListener('contextmenu', (event) => event.preventDefault()) @@ -137,9 +131,7 @@ const handleClose = async () => { const router = useRouter() router.afterEach((to, from, failure) => { - if (mixpanel_is_loaded()) { - mixpanel_track('PageView', { path: to.path, fromPath: from.path, failed: failure }) - } + trackEvent('PageView', { path: to.path, fromPath: from.path, failed: failure }) }) const route = useRoute() const isOnBrowse = computed(() => route.path.startsWith('/browse')) @@ -214,7 +206,7 @@ async function handleCommand(e) { // RunMRPack should directly install a local mrpack given a path if (e.path.endsWith('.mrpack')) { await install_from_file(e.path).catch(handleError) - mixpanel_track('InstanceCreate', { + trackEvent('InstanceCreate', { source: 'CreationModalFileDrop', }) } diff --git a/apps/app-frontend/src/components/RowDisplay.vue b/apps/app-frontend/src/components/RowDisplay.vue index ff42de96f..10776cd0a 100644 --- a/apps/app-frontend/src/components/RowDisplay.vue +++ b/apps/app-frontend/src/components/RowDisplay.vue @@ -23,7 +23,7 @@ import { duplicate, kill, remove, run } from '@/helpers/profile.js' import { useRouter } from 'vue-router' import { showProfileInFolder } from '@/helpers/utils.js' import { useTheming } from '@/store/state.js' -import { mixpanel_track } from '@/helpers/mixpanel' +import { trackEvent } from '@/helpers/analytics' import { handleSevereError } from '@/store/error.js' import { install as installVersion } from '@/store/install.js' @@ -125,14 +125,14 @@ const handleOptionsClick = async (args) => { await run(args.item.path).catch((err) => handleSevereError(err, { profilePath: args.item.path }), ) - mixpanel_track('InstanceStart', { + trackEvent('InstanceStart', { loader: args.item.loader, game_version: args.item.game_version, }) break case 'stop': await kill(args.item.path).catch(handleError) - mixpanel_track('InstanceStop', { + trackEvent('InstanceStop', { loader: args.item.loader, game_version: args.item.game_version, }) diff --git a/apps/app-frontend/src/components/ui/AccountsCard.vue b/apps/app-frontend/src/components/ui/AccountsCard.vue index acfaa3a69..1a6225777 100644 --- a/apps/app-frontend/src/components/ui/AccountsCard.vue +++ b/apps/app-frontend/src/components/ui/AccountsCard.vue @@ -70,7 +70,7 @@ import { get_default_user, } from '@/helpers/auth' import { handleError } from '@/store/state.js' -import { mixpanel_track } from '@/helpers/mixpanel' +import { trackEvent } from '@/helpers/analytics' import { process_listener } from '@/helpers/events' import { handleSevereError } from '@/store/error.js' @@ -118,7 +118,7 @@ async function login() { await refreshValues() } - mixpanel_track('AccountLogIn') + trackEvent('AccountLogIn') } const logout = async (id) => { @@ -130,7 +130,7 @@ const logout = async (id) => { } else { emit('change') } - mixpanel_track('AccountLogOut') + trackEvent('AccountLogOut') } let showCard = ref(false) diff --git a/apps/app-frontend/src/components/ui/ErrorModal.vue b/apps/app-frontend/src/components/ui/ErrorModal.vue index 4d8ac6b8c..5bebed0f3 100644 --- a/apps/app-frontend/src/components/ui/ErrorModal.vue +++ b/apps/app-frontend/src/components/ui/ErrorModal.vue @@ -5,10 +5,10 @@ import { ChatIcon } from '@/assets/icons' import { ref } from 'vue' import { login as login_flow, set_default_user } from '@/helpers/auth.js' import { handleError } from '@/store/notifications.js' -import mixpanel from 'mixpanel-browser' import { handleSevereError } from '@/store/error.js' import { cancel_directory_change } from '@/helpers/settings.js' import { install } from '@/helpers/profile.js' +import { trackEvent } from '@/helpers/analytics' const errorModal = ref() const error = ref() @@ -85,7 +85,7 @@ async function loginMinecraft() { await set_default_user(loggedIn.id).catch(handleError) } - await mixpanel.track('AccountLogIn') + await trackEvent('AccountLogIn', { source: 'ErrorModal' }) loadingMinecraft.value = false errorModal.value.hide() } catch (err) { diff --git a/apps/app-frontend/src/components/ui/Instance.vue b/apps/app-frontend/src/components/ui/Instance.vue index 81eec9098..59a2e01ad 100644 --- a/apps/app-frontend/src/components/ui/Instance.vue +++ b/apps/app-frontend/src/components/ui/Instance.vue @@ -9,8 +9,8 @@ import { get_by_profile_path } from '@/helpers/process' import { process_listener } from '@/helpers/events' import { handleError } from '@/store/state.js' import { showProfileInFolder } from '@/helpers/utils.js' -import { mixpanel_track } from '@/helpers/mixpanel' import { handleSevereError } from '@/store/error.js' +import { trackEvent } from '@/helpers/analytics' const props = defineProps({ instance: { @@ -45,7 +45,7 @@ const play = async (e, context) => { ) modLoading.value = false - mixpanel_track('InstancePlay', { + trackEvent('InstancePlay', { loader: props.instance.loader, game_version: props.instance.game_version, source: context, @@ -58,7 +58,7 @@ const stop = async (e, context) => { await kill(props.instance.path).catch(handleError) - mixpanel_track('InstanceStop', { + trackEvent('InstanceStop', { loader: props.instance.loader, game_version: props.instance.game_version, source: context, diff --git a/apps/app-frontend/src/components/ui/InstanceCreationModal.vue b/apps/app-frontend/src/components/ui/InstanceCreationModal.vue index 2c549f7c0..f19c20146 100644 --- a/apps/app-frontend/src/components/ui/InstanceCreationModal.vue +++ b/apps/app-frontend/src/components/ui/InstanceCreationModal.vue @@ -216,7 +216,7 @@ import { convertFileSrc } from '@tauri-apps/api/core' import { get_game_versions, get_loader_versions } from '@/helpers/metadata' import { handleError } from '@/store/notifications.js' import Multiselect from 'vue-multiselect' -import { mixpanel_track } from '@/helpers/mixpanel' +import { trackEvent } from '@/helpers/analytics' import { useTheming } from '@/store/state.js' import { listen } from '@tauri-apps/api/event' import { install_from_file } from '@/helpers/pack.js' @@ -264,13 +264,13 @@ defineExpose({ hide() if (event.payload && event.payload.length > 0 && event.payload[0].endsWith('.mrpack')) { await install_from_file(event.payload[0]).catch(handleError) - mixpanel_track('InstanceCreate', { + trackEvent('InstanceCreate', { source: 'CreationModalFileDrop', }) } }) - mixpanel_track('InstanceCreateStart', { source: 'CreationModal' }) + trackEvent('InstanceCreateStart', { source: 'CreationModal' }) }, }) @@ -360,7 +360,7 @@ const create_instance = async () => { icon.value, ).catch(handleError) - mixpanel_track('InstanceCreate', { + trackEvent('InstanceCreate', { profile_name: profile_name.value, game_version: game_version.value, loader: loader.value, @@ -419,7 +419,7 @@ const openFile = async () => { hide() await install_from_file(newProject).catch(handleError) - mixpanel_track('InstanceCreate', { + trackEvent('InstanceCreate', { source: 'CreationModalFileOpen', }) } diff --git a/apps/app-frontend/src/components/ui/JavaDetectionModal.vue b/apps/app-frontend/src/components/ui/JavaDetectionModal.vue index ac68c224d..fef785dc6 100644 --- a/apps/app-frontend/src/components/ui/JavaDetectionModal.vue +++ b/apps/app-frontend/src/components/ui/JavaDetectionModal.vue @@ -40,8 +40,8 @@ import { Modal, Button } from '@modrinth/ui' import { ref } from 'vue' import { find_filtered_jres } from '@/helpers/jre.js' import { handleError } from '@/store/notifications.js' -import { mixpanel_track } from '@/helpers/mixpanel' import { useTheming } from '@/store/theme.js' +import { trackEvent } from '@/helpers/analytics' const themeStore = useTheming() @@ -67,7 +67,7 @@ const emit = defineEmits(['submit']) function setJavaInstall(javaInstall) { emit('submit', javaInstall) detectJavaModal.value.hide() - mixpanel_track('JavaAutoDetect', { + trackEvent('JavaAutoDetect', { path: javaInstall.path, version: javaInstall.version, }) diff --git a/apps/app-frontend/src/components/ui/JavaSelector.vue b/apps/app-frontend/src/components/ui/JavaSelector.vue index 0a7111e13..aeb292fdd 100644 --- a/apps/app-frontend/src/components/ui/JavaSelector.vue +++ b/apps/app-frontend/src/components/ui/JavaSelector.vue @@ -65,8 +65,8 @@ import { auto_install_java, find_filtered_jres, get_jre, test_jre } from '@/help import { ref } from 'vue' import { open } from '@tauri-apps/plugin-dialog' import JavaDetectionModal from '@/components/ui/JavaDetectionModal.vue' -import { mixpanel_track } from '@/helpers/mixpanel' import { handleError } from '@/store/state.js' +import { trackEvent } from '@/helpers/analytics' const props = defineProps({ version: { @@ -113,7 +113,7 @@ async function testJava() { ) testingJava.value = false - mixpanel_track('JavaTest', { + trackEvent('JavaTest', { path: props.modelValue ? props.modelValue.path : '', success: testingJavaSuccess.value, }) @@ -136,7 +136,7 @@ async function handleJavaFileInput() { } } - mixpanel_track('JavaManualSelect', { + trackEvent('JavaManualSelect', { path: filePath, version: props.version, }) @@ -170,7 +170,7 @@ async function reinstallJava() { } } - mixpanel_track('JavaReInstall', { + trackEvent('JavaReInstall', { path: path, version: props.version, }) diff --git a/apps/app-frontend/src/components/ui/RunningAppBar.vue b/apps/app-frontend/src/components/ui/RunningAppBar.vue index 2010c98dc..ad600fc7f 100644 --- a/apps/app-frontend/src/components/ui/RunningAppBar.vue +++ b/apps/app-frontend/src/components/ui/RunningAppBar.vue @@ -117,9 +117,9 @@ import { useRouter } from 'vue-router' import { progress_bars_list } from '@/helpers/state.js' import ProgressBar from '@/components/ui/ProgressBar.vue' import { handleError } from '@/store/notifications.js' -import { mixpanel_track } from '@/helpers/mixpanel' import { ChatIcon } from '@/assets/icons' import { get_many } from '@/helpers/profile.js' +import { trackEvent } from '@/helpers/analytics' const router = useRouter() const card = ref(null) @@ -164,7 +164,7 @@ const stop = async (process) => { try { await killProcess(process.uuid).catch(handleError) - mixpanel_track('InstanceStop', { + trackEvent('InstanceStop', { loader: process.profile.loader, game_version: process.profile.game_version, source: 'AppBar', diff --git a/apps/app-frontend/src/components/ui/install_flow/IncompatibilityWarningModal.vue b/apps/app-frontend/src/components/ui/install_flow/IncompatibilityWarningModal.vue index d78bcae39..030eeccc0 100644 --- a/apps/app-frontend/src/components/ui/install_flow/IncompatibilityWarningModal.vue +++ b/apps/app-frontend/src/components/ui/install_flow/IncompatibilityWarningModal.vue @@ -61,7 +61,7 @@ import { formatCategory } from '@modrinth/utils' import { add_project_from_version as installMod } from '@/helpers/profile' import { ref } from 'vue' import { handleError, useTheming } from '@/store/state.js' -import { mixpanel_track } from '@/helpers/mixpanel' +import { trackEvent } from '@/helpers/analytics' const themeStore = useTheming() @@ -87,7 +87,7 @@ defineExpose({ incompatibleModal.value.show() - mixpanel_track('ProjectInstallStart', { source: 'ProjectIncompatibilityWarningModal' }) + trackEvent('ProjectInstallStart', { source: 'ProjectIncompatibilityWarningModal' }) }, }) @@ -98,7 +98,7 @@ const install = async () => { onInstall.value(selectedVersion.value.id) incompatibleModal.value.hide() - mixpanel_track('ProjectInstall', { + trackEvent('ProjectInstall', { loader: instance.value.loader, game_version: instance.value.game_version, id: project.value, diff --git a/apps/app-frontend/src/components/ui/install_flow/InstallConfirmModal.vue b/apps/app-frontend/src/components/ui/install_flow/InstallConfirmModal.vue index 3d36a9be8..0c75a0677 100644 --- a/apps/app-frontend/src/components/ui/install_flow/InstallConfirmModal.vue +++ b/apps/app-frontend/src/components/ui/install_flow/InstallConfirmModal.vue @@ -3,7 +3,7 @@ import { XIcon, DownloadIcon } from '@modrinth/assets' import { Button, Modal } from '@modrinth/ui' import { install as pack_install } from '@/helpers/pack' import { ref } from 'vue' -import { mixpanel_track } from '@/helpers/mixpanel' +import { trackEvent } from '@/helpers/analytics' import { useTheming } from '@/store/theme.js' import { handleError } from '@/store/state.js' @@ -25,7 +25,7 @@ defineExpose({ onInstall.value = callback - mixpanel_track('PackInstallStart') + trackEvent('PackInstallStart') }, }) @@ -39,7 +39,7 @@ async function install() { project.value.title, project.value.icon_url, ).catch(handleError) - mixpanel_track('PackInstall', { + trackEvent('PackInstall', { id: project.value.id, version_id: versionId.value, title: project.value.title, diff --git a/apps/app-frontend/src/components/ui/install_flow/ModInstallModal.vue b/apps/app-frontend/src/components/ui/install_flow/ModInstallModal.vue index 7ca3b53ef..1ac6a5503 100644 --- a/apps/app-frontend/src/components/ui/install_flow/ModInstallModal.vue +++ b/apps/app-frontend/src/components/ui/install_flow/ModInstallModal.vue @@ -19,10 +19,10 @@ import { import { open } from '@tauri-apps/plugin-dialog' import { installVersionDependencies } from '@/store/install.js' import { handleError } from '@/store/notifications.js' -import { mixpanel_track } from '@/helpers/mixpanel' import { useTheming } from '@/store/theme.js' import { useRouter } from 'vue-router' import { convertFileSrc } from '@tauri-apps/api/core' +import { trackEvent } from '@/helpers/analytics' const themeStore = useTheming() const router = useRouter() @@ -88,7 +88,7 @@ defineExpose({ installModal.value.show() - mixpanel_track('ProjectInstallStart', { source: 'ProjectInstallModal' }) + trackEvent('ProjectInstallStart', { source: 'ProjectInstallModal' }) }, }) @@ -115,7 +115,7 @@ async function install(instance) { instance.installedMod = true instance.installing = false - mixpanel_track('ProjectInstall', { + trackEvent('ProjectInstall', { loader: instance.loader, game_version: instance.game_version, id: project.value.id, @@ -137,7 +137,7 @@ const toggleCreation = () => { loader.value = null if (showCreation.value) { - mixpanel_track('InstanceCreateStart', { source: 'ProjectInstallModal' }) + trackEvent('InstanceCreateStart', { source: 'ProjectInstallModal' }) } } @@ -186,7 +186,7 @@ const createInstance = async () => { const instance = await get(id, true) await installVersionDependencies(instance, versions.value[0]) - mixpanel_track('InstanceCreate', { + trackEvent('InstanceCreate', { profile_name: name.value, game_version: versions.value[0].game_versions[0], loader: loader, @@ -195,7 +195,7 @@ const createInstance = async () => { source: 'ProjectInstallModal', }) - mixpanel_track('ProjectInstall', { + trackEvent('ProjectInstall', { loader: loader, game_version: versions.value[0].game_versions[0], id: project.value, diff --git a/apps/app-frontend/src/helpers/analytics.js b/apps/app-frontend/src/helpers/analytics.js new file mode 100644 index 000000000..90d2c8aef --- /dev/null +++ b/apps/app-frontend/src/helpers/analytics.js @@ -0,0 +1,23 @@ +import { posthog } from 'posthog-js' + +export const initAnalytics = () => { + posthog.init('phc_hm2ihMpTAoE86xIm7XzsCB8RPiTRKivViK5biiHedm', { + persistence: 'localStorage', + }) +} + +export const debugAnalytics = () => { + posthog.debug() +} + +export const optOutAnalytics = () => { + posthog.opt_out_capturing() +} + +export const optInAnalytics = () => { + posthog.opt_in_capturing() +} + +export const trackEvent = (eventName, properties) => { + posthog.capture(eventName, properties) +} diff --git a/apps/app-frontend/src/helpers/mixpanel.js b/apps/app-frontend/src/helpers/mixpanel.js deleted file mode 100644 index d527ea2ad..000000000 --- a/apps/app-frontend/src/helpers/mixpanel.js +++ /dev/null @@ -1,57 +0,0 @@ -import mixpanel from 'mixpanel-browser' - -// mixpanel_track -function trackWrapper(originalTrack) { - return function (event_name, properties = {}) { - try { - originalTrack(event_name, properties) - } catch (e) { - console.error(e) - } - } -} -export const mixpanel_track = trackWrapper(mixpanel.track.bind(mixpanel)) - -// mixpanel_opt_out_tracking() -function optOutTrackingWrapper(originalOptOutTracking) { - return function () { - try { - originalOptOutTracking() - } catch (e) { - console.error(e) - } - } -} -export const mixpanel_opt_out_tracking = optOutTrackingWrapper( - mixpanel.opt_out_tracking.bind(mixpanel), -) - -// mixpanel_opt_in_tracking() -function optInTrackingWrapper(originalOptInTracking) { - return function () { - try { - originalOptInTracking() - } catch (e) { - console.error(e) - } - } -} -export const mixpanel_opt_in_tracking = optInTrackingWrapper( - mixpanel.opt_in_tracking.bind(mixpanel), -) - -// mixpanel_init -function initWrapper(originalInit) { - return function (token, config = {}) { - try { - originalInit(token, config) - } catch (e) { - console.error(e) - } - } -} -export const mixpanel_init = initWrapper(mixpanel.init.bind(mixpanel)) - -export const mixpanel_is_loaded = () => { - return mixpanel.__loaded -} diff --git a/apps/app-frontend/src/main.js b/apps/app-frontend/src/main.js index 04d4efdbe..2262d1a5c 100644 --- a/apps/app-frontend/src/main.js +++ b/apps/app-frontend/src/main.js @@ -5,6 +5,7 @@ import { createPinia } from 'pinia' import FloatingVue from 'floating-vue' import 'floating-vue/dist/style.css' import { createPlugin } from '@vintl/vintl/plugin' +import * as Sentry from '@sentry/vue' const VIntlPlugin = createPlugin({ controllerOpts: { @@ -26,6 +27,14 @@ const VIntlPlugin = createPlugin({ const pinia = createPinia() let app = createApp(App) + +Sentry.init({ + app, + dsn: 'https://9508775ee5034536bc70433f5f531dd4@o485889.ingest.us.sentry.io/4504579615227904', + integrations: [Sentry.browserTracingIntegration({ router })], + tracesSampleRate: 0.1, +}) + app.use(router) app.use(pinia) app.use(FloatingVue) diff --git a/apps/app-frontend/src/pages/Settings.vue b/apps/app-frontend/src/pages/Settings.vue index 255262a34..e308e7f51 100644 --- a/apps/app-frontend/src/pages/Settings.vue +++ b/apps/app-frontend/src/pages/Settings.vue @@ -8,7 +8,7 @@ import { get_java_versions, get_max_memory, set_java_version } from '@/helpers/j import { get as getCreds, logout } from '@/helpers/mr_auth.js' import JavaSelector from '@/components/ui/JavaSelector.vue' import ModrinthLoginScreen from '@/components/ui/tutorial/ModrinthLoginScreen.vue' -import { mixpanel_opt_out_tracking, mixpanel_opt_in_tracking } from '@/helpers/mixpanel' +import { optOutAnalytics, optInAnalytics } from '@/helpers/analytics' import { open } from '@tauri-apps/plugin-dialog' import { getOS } from '@/helpers/utils.js' import { getVersion } from '@tauri-apps/api/app' @@ -45,9 +45,9 @@ watch( const setSettings = JSON.parse(JSON.stringify(newSettings)) if (setSettings.telemetry) { - mixpanel_opt_out_tracking() + optInAnalytics() } else { - mixpanel_opt_in_tracking() + optOutAnalytics() } setSettings.extra_launch_args = setSettings.launchArgs.trim().split(/\s+/).filter(Boolean) diff --git a/apps/app-frontend/src/pages/instance/Index.vue b/apps/app-frontend/src/pages/instance/Index.vue index 920bda4e7..9f607fe49 100644 --- a/apps/app-frontend/src/pages/instance/Index.vue +++ b/apps/app-frontend/src/pages/instance/Index.vue @@ -131,9 +131,8 @@ import { ref, onUnmounted } from 'vue' import { handleError, useBreadcrumbs, useLoading } from '@/store/state' import { showProfileInFolder } from '@/helpers/utils.js' import ContextMenu from '@/components/ui/ContextMenu.vue' -import { mixpanel_track } from '@/helpers/mixpanel' +import { trackEvent } from '@/helpers/analytics' import { convertFileSrc } from '@tauri-apps/api/core' -import { useFetch } from '@/helpers/fetch' import { handleSevereError } from '@/store/error.js' import { get_project, get_version_many } from '@/helpers/cache.js' import dayjs from 'dayjs' @@ -183,7 +182,7 @@ const startInstance = async (context) => { } loading.value = false - mixpanel_track('InstanceStart', { + trackEvent('InstanceStart', { loader: instance.value.loader, game_version: instance.value.game_version, source: context, @@ -220,7 +219,7 @@ const stopInstance = async (context) => { playing.value = false await kill(route.params.id).catch(handleError) - mixpanel_track('InstanceStop', { + trackEvent('InstanceStop', { loader: instance.value.loader, game_version: instance.value.game_version, source: context, diff --git a/apps/app-frontend/src/pages/instance/Mods.vue b/apps/app-frontend/src/pages/instance/Mods.vue index a1972bcfc..a0fac26de 100644 --- a/apps/app-frontend/src/pages/instance/Mods.vue +++ b/apps/app-frontend/src/pages/instance/Mods.vue @@ -379,7 +379,7 @@ import { update_project, } from '@/helpers/profile.js' import { handleError } from '@/store/notifications.js' -import { mixpanel_track } from '@/helpers/mixpanel' +import { trackEvent } from '@/helpers/analytics' import { listen } from '@tauri-apps/api/event' import { highlightModInProfile } from '@/helpers/utils.js' import { MenuIcon, ToggleIcon, TextInputIcon, AddProjectImage, PackageIcon } from '@/assets/icons' @@ -682,7 +682,7 @@ const updateAll = async () => { projects.value[project].updating = false } - mixpanel_track('InstanceUpdateAll', { + trackEvent('InstanceUpdateAll', { loader: props.instance.loader, game_version: props.instance.game_version, count: setProjects.length, @@ -708,7 +708,7 @@ const updateProject = async (mod) => { mod.version = mod.updateVersion.version_number mod.updateVersion = null - mixpanel_track('InstanceProjectUpdate', { + trackEvent('InstanceProjectUpdate', { loader: props.instance.loader, game_version: props.instance.game_version, id: mod.id, @@ -735,7 +735,7 @@ const toggleDisableMod = async (mod) => { .then((newPath) => { mod.path = newPath mod.disabled = !mod.disabled - mixpanel_track('InstanceProjectDisable', { + trackEvent('InstanceProjectDisable', { loader: props.instance.loader, game_version: props.instance.game_version, id: mod.id, @@ -756,7 +756,7 @@ const removeMod = async (mod) => { await remove_project(props.instance.path, mod.path).catch(handleError) projects.value = projects.value.filter((x) => mod.path !== x.path) - mixpanel_track('InstanceProjectRemove', { + trackEvent('InstanceProjectRemove', { loader: props.instance.loader, game_version: props.instance.game_version, id: mod.id, diff --git a/apps/app-frontend/src/pages/instance/Options.vue b/apps/app-frontend/src/pages/instance/Options.vue index 220c42c8d..302ca9b60 100644 --- a/apps/app-frontend/src/pages/instance/Options.vue +++ b/apps/app-frontend/src/pages/instance/Options.vue @@ -546,10 +546,10 @@ import { open } from '@tauri-apps/plugin-dialog' import { get_loader_versions } from '@/helpers/metadata.js' import { get_game_versions, get_loaders } from '@/helpers/tags.js' import { handleError } from '@/store/notifications.js' -import { mixpanel_track } from '@/helpers/mixpanel' import { useTheming } from '@/store/theme.js' import { useBreadcrumbs } from '@/store/breadcrumbs' import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue' +import { trackEvent } from '@/helpers/analytics' const breadcrumbs = useBreadcrumbs() @@ -590,7 +590,7 @@ const availableGroups = ref([ async function resetIcon() { icon.value = null await edit_icon(props.instance.path, null).catch(handleError) - mixpanel_track('InstanceRemoveIcon') + trackEvent('InstanceRemoveIcon') } async function setIcon() { @@ -609,7 +609,7 @@ async function setIcon() { icon.value = value await edit_icon(props.instance.path, icon.value).catch(handleError) - mixpanel_track('InstanceSetIcon') + trackEvent('InstanceSetIcon') } const globalSettings = await get().catch(handleError) @@ -754,7 +754,7 @@ const repairing = ref(false) async function duplicateProfile() { await duplicate(props.instance.path).catch(handleError) - mixpanel_track('InstanceDuplicate', { + trackEvent('InstanceDuplicate', { loader: props.instance.loader, game_version: props.instance.game_version, }) @@ -765,7 +765,7 @@ async function repairProfile(force) { await install(props.instance.path, force).catch(handleError) repairing.value = false - mixpanel_track('InstanceRepair', { + trackEvent('InstanceRepair', { loader: props.instance.loader, game_version: props.instance.game_version, }) @@ -796,7 +796,7 @@ async function repairModpack() { await update_repair_modrinth(props.instance.path).catch(handleError) inProgress.value = false - mixpanel_track('InstanceRepair', { + trackEvent('InstanceRepair', { loader: props.instance.loader, game_version: props.instance.game_version, }) @@ -808,7 +808,7 @@ async function removeProfile() { await remove(props.instance.path).catch(handleError) removing.value = false - mixpanel_track('InstanceRemove', { + trackEvent('InstanceRemove', { loader: props.instance.loader, game_version: props.instance.game_version, }) diff --git a/apps/app-frontend/src/pages/project/Gallery.vue b/apps/app-frontend/src/pages/project/Gallery.vue index 1a2b3ff8c..b2b96fb41 100644 --- a/apps/app-frontend/src/pages/project/Gallery.vue +++ b/apps/app-frontend/src/pages/project/Gallery.vue @@ -93,7 +93,7 @@ import { } from '@modrinth/assets' import { Button, Card } from '@modrinth/ui' import { ref } from 'vue' -import { mixpanel_track } from '@/helpers/mixpanel' +import { trackEvent } from '@/helpers/analytics' const props = defineProps({ project: { @@ -112,7 +112,7 @@ const nextImage = () => { expandedGalleryIndex.value = 0 } expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value] - mixpanel_track('GalleryImageNext', { + trackEvent('GalleryImageNext', { project_id: props.project.id, url: expandedGalleryItem.value.url, }) @@ -124,7 +124,7 @@ const previousImage = () => { expandedGalleryIndex.value = props.project.gallery.length - 1 } expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value] - mixpanel_track('GalleryImagePrevious', { + trackEvent('GalleryImagePrevious', { project_id: props.project.id, url: expandedGalleryItem.value, }) @@ -135,7 +135,7 @@ const expandImage = (item, index) => { expandedGalleryIndex.value = index zoomedIn.value = false - mixpanel_track('GalleryImageExpand', { + trackEvent('GalleryImageExpand', { project_id: props.project.id, url: item.url, }) diff --git a/apps/app-frontend/src/store/install.js b/apps/app-frontend/src/store/install.js index 1363de754..e6562ab65 100644 --- a/apps/app-frontend/src/store/install.js +++ b/apps/app-frontend/src/store/install.js @@ -10,7 +10,7 @@ import { import { handleError } from '@/store/notifications.js' import { get_project, get_version_many } from '@/helpers/cache.js' import { install as packInstall } from '@/helpers/pack.js' -import { mixpanel_track } from '@/helpers/mixpanel.js' +import { trackEvent } from '@/helpers/analytics.js' import dayjs from 'dayjs' export const useInstall = defineStore('installStore', { @@ -51,7 +51,7 @@ export const install = async (projectId, versionId, instancePath, source, callba if (packs.length === 0 || !packs.find((pack) => pack.linked_data?.project_id === project.id)) { await packInstall(project.id, version, project.title, project.icon_url).catch(handleError) - mixpanel_track('PackInstall', { + trackEvent('PackInstall', { id: project.id, version_id: version, title: project.title, @@ -107,7 +107,7 @@ export const install = async (projectId, versionId, instancePath, source, callba await add_project_from_version(instance.path, version.id).catch(handleError) await installVersionDependencies(instance, version) - mixpanel_track('ProjectInstall', { + trackEvent('ProjectInstall', { loader: instance.loader, game_version: instance.game_version, id: project.id, diff --git a/apps/app-frontend/src/store/notifications.js b/apps/app-frontend/src/store/notifications.js index f5d5e4540..52cc53476 100644 --- a/apps/app-frontend/src/store/notifications.js +++ b/apps/app-frontend/src/store/notifications.js @@ -23,7 +23,3 @@ export const handleError = (err) => { }) console.error(err) } - -export const handleMixpanelError = (err) => { - console.error(err) -} diff --git a/apps/app/tauri.conf.json b/apps/app/tauri.conf.json index b48c7d3c5..c9f7834dc 100644 --- a/apps/app/tauri.conf.json +++ b/apps/app/tauri.conf.json @@ -86,7 +86,7 @@ ], "enable": true }, - "csp": "default-src 'self'; connect-src ipc: http://ipc.localhost https://modrinth.com https://*.modrinth.com https://mixpanel.com https://*.mixpanel.com https://*.cloudflare.com https://api.mclo.gs; font-src https://cdn-raw.modrinth.com/fonts/inter/; img-src tauri: https: data: blob: 'unsafe-inline' asset: https://asset.localhost; script-src https://*.cloudflare.com 'self'; frame-src https://*.cloudflare.com https://www.youtube.com https://www.youtube-nocookie.com https://discord.com 'self'; style-src unsafe-inline 'self'" + "csp": "default-src 'self'; connect-src ipc: http://ipc.localhost https://modrinth.com https://*.modrinth.com https://*.posthog.com https://*.sentry.io https://*.cloudflare.com https://api.mclo.gs; font-src https://cdn-raw.modrinth.com/fonts/inter/; img-src tauri: https: data: blob: 'unsafe-inline' asset: https://asset.localhost; script-src https://*.cloudflare.com 'self'; frame-src https://*.cloudflare.com https://www.youtube.com https://www.youtube-nocookie.com https://discord.com 'self'; style-src unsafe-inline 'self'" } } } diff --git a/apps/frontend/src/pages/legal/privacy.vue b/apps/frontend/src/pages/legal/privacy.vue index 9196d9b78..82214716d 100644 --- a/apps/frontend/src/pages/legal/privacy.vue +++ b/apps/frontend/src/pages/legal/privacy.vue @@ -109,7 +109,7 @@

This data is used to deliver statistics.

Usage data

-

When you interact with the Modrinth App or the Website, we collect through MixPanel:

+

When you interact with the Modrinth App or the Website, we collect through PostHog:

Data that we specifically collect isn't shared with any other third party. We do not sell any diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 83f577325..ab37fdeff 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: '@modrinth/utils': specifier: workspace:* version: link:../../packages/utils + '@sentry/vue': + specifier: ^8.27.0 + version: 8.27.0(vue@3.4.31(typescript@5.5.3)) '@tauri-apps/api': specifier: ^2.0.0-rc.3 version: 2.0.0-rc.3 @@ -71,15 +74,15 @@ importers: floating-vue: specifier: ^5.2.2 version: 5.2.2(@nuxt/kit@3.12.3)(vue@3.4.31(typescript@5.5.3)) - mixpanel-browser: - specifier: ^2.49.0 - version: 2.53.0 ofetch: specifier: ^1.3.4 version: 1.3.4 pinia: specifier: ^2.1.7 version: 2.1.7(typescript@5.5.3)(vue@3.4.31(typescript@5.5.3)) + posthog-js: + specifier: ^1.158.2 + version: 1.158.2 vite-svg-loader: specifier: ^5.1.0 version: 5.1.0(vue@3.4.31(typescript@5.5.3)) @@ -1900,8 +1903,43 @@ packages: cpu: [x64] os: [win32] - '@rrweb/types@2.0.0-alpha.16': - resolution: {integrity: sha512-E6cACNVsm+NUhn7dzocQoKyXI7BHrHRRm5Ab23yrAzEQ2caWocCEYJhqDlc4KRVJBkQfXZfyWm8+2d0uggFuZg==} + '@sentry-internal/browser-utils@8.27.0': + resolution: {integrity: sha512-YTIwQ1GM1NTRXgN4DvpFSQ2x4pjlqQ0FQAyHW5x2ZYv4z7VmqG4Xkid1P/srQUipECk6nxkebfD4WR19nLsvnQ==} + engines: {node: '>=14.18'} + + '@sentry-internal/feedback@8.27.0': + resolution: {integrity: sha512-b71PQc9aK1X9b/SO1DiJlrnAEx4n0MzPZQ/tKd9oRWDyGit6pJWZfQns9r2rvc96kJPMOTxFAa/upXRCkA723A==} + engines: {node: '>=14.18'} + + '@sentry-internal/replay-canvas@8.27.0': + resolution: {integrity: sha512-uuEfiWbjwugB9M4KxXxovHYiKRqg/R6U4EF8xM/Ub4laUuEcWsfRp7lQ3MxL3qYojbca8ncIFic2bIoKMPeejA==} + engines: {node: '>=14.18'} + + '@sentry-internal/replay@8.27.0': + resolution: {integrity: sha512-Ofucncaon98dvlxte2L//hwuG9yILSxNrTz/PmO0k+HzB9q+oBic4667QF+azWR2qv4oKSWpc+vEovP3hVqveA==} + engines: {node: '>=14.18'} + + '@sentry/browser@8.27.0': + resolution: {integrity: sha512-eL1eaHwoYUGkp4mpeYesH6WtCrm+0u9jYCW5Lm0MAeTmpx22BZKEmj0OljuUJXGnJwFbvPDlRjyz6QG11m8kZA==} + engines: {node: '>=14.18'} + + '@sentry/core@8.27.0': + resolution: {integrity: sha512-4frlXluHT3Du+Omw91K04jpvbfMtydvg4Bxj2+gt/DT19Swhm/fbEpzdUjgbAd3Jinj/n0qk/jFRXjr9JZKFjg==} + engines: {node: '>=14.18'} + + '@sentry/types@8.27.0': + resolution: {integrity: sha512-B6lrP46+m2x0lfqWc9F4VcUbN893mVGnPEd7KIMRk95mPzkFJ3sNxggTQF5/ZfNO7lDQYQb22uysB5sj/BqFiw==} + engines: {node: '>=14.18'} + + '@sentry/utils@8.27.0': + resolution: {integrity: sha512-gyJM3SyLQe0A3mkQVVNdKYvk3ZoikkYgyA/D+5StFNLKdyUgEbJgXOGXrQSSYPF7BSX6Sc5b0KHCglPII0KuKw==} + engines: {node: '>=14.18'} + + '@sentry/vue@8.27.0': + resolution: {integrity: sha512-kCjrdKCQk9ZgE7HirVaT/hvyBhoryEHickiWQET7fzyEo6Zs7/KoFnNiXzGuZ+XJcZ8R76wlog+awBBmZBuBsQ==} + engines: {node: '>=14.18'} + peerDependencies: + vue: 2.x || 3.x '@sindresorhus/merge-streams@2.3.0': resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} @@ -2056,9 +2094,6 @@ packages: resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} - '@types/css-font-loading-module@0.0.7': - resolution: {integrity: sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==} - '@types/eslint-scope@3.7.7': resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} @@ -2470,9 +2505,6 @@ packages: '@webassemblyjs/wast-printer@1.12.1': resolution: {integrity: sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==} - '@xstate/fsm@1.6.5': - resolution: {integrity: sha512-b5o1I6aLNeYlU/3CPlj/Z91ybk1gUsKT+5NAJI+2W4UjvS5KLG28K9v5UvNoFVjHV8PajVZ00RH3vnjyQO7ZAw==} - '@xtuc/ieee754@1.2.0': resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -2651,10 +2683,6 @@ packages: bare-events@2.4.2: resolution: {integrity: sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==} - base64-arraybuffer@1.0.2: - resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} - engines: {node: '>= 0.6.0'} - base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -4335,9 +4363,6 @@ packages: mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - mixpanel-browser@2.53.0: - resolution: {integrity: sha512-8U7zCTT82yCIH2vfdCvs0ZRWlCgyHMuU4jtC6yOAiNUR4HhnQYk7re/o2GnhfdvYtkPxdda60/3eH1igUlIXuw==} - mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} @@ -4887,6 +4912,12 @@ packages: resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==} engines: {node: ^10 || ^12 || >=14} + posthog-js@1.158.2: + resolution: {integrity: sha512-ovb7GHHRNDf6vmuL+8lbDukewzDzQlLZXg3d475hrfHSBgidYeTxtLGtoBcUz4x6558BLDFjnSip+f3m4rV9LA==} + + preact@10.23.2: + resolution: {integrity: sha512-kKYfePf9rzKnxOAKDpsWhg/ysrHPqT+yQ7UW4JjdnqjFIeNUnNcEJvhuA8fDenxAGWzUqtd51DfVg7xp/8T9NA==} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -5114,15 +5145,6 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rrdom@2.0.0-alpha.16: - resolution: {integrity: sha512-m8aoeORWUz7AFdEb7hES7wPeL6fl/oP23RoAlzLXyA/f2+NqCDM7KEyCXY4sHu6CChN3OAUP2BaUGEXn0zynlw==} - - rrweb-snapshot@2.0.0-alpha.16: - resolution: {integrity: sha512-p81OrzUiCmUMZzJu4fGHeLB00PIbVIqsV/zhqzr2pitHTUXpMYcyOvDWt0vHdla0vnowEPaHq3Wsu6cUc732/w==} - - rrweb@2.0.0-alpha.13: - resolution: {integrity: sha512-a8GXOCnzWHNaVZPa7hsrLZtNZ3CGjiL+YrkpLo0TfmxGLhjNZbWY2r7pE06p+FcjFNlgUVTmFrSJbK3kO7yxvw==} - run-applescript@7.0.0: resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} engines: {node: '>=18'} @@ -5974,6 +5996,9 @@ packages: resolution: {integrity: sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==} engines: {node: '>=10.13.0'} + web-vitals@4.2.3: + resolution: {integrity: sha512-/CFAm1mNxSmOj6i0Co+iGFJ58OS4NRGVP+AWS/l509uIK5a1bSoIVaHz/ZumpHTfHSZBpgrJ+wjfpAOrTHok5Q==} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -7699,9 +7724,60 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.18.0': optional: true - '@rrweb/types@2.0.0-alpha.16': + '@sentry-internal/browser-utils@8.27.0': + dependencies: + '@sentry/core': 8.27.0 + '@sentry/types': 8.27.0 + '@sentry/utils': 8.27.0 + + '@sentry-internal/feedback@8.27.0': dependencies: - rrweb-snapshot: 2.0.0-alpha.16 + '@sentry/core': 8.27.0 + '@sentry/types': 8.27.0 + '@sentry/utils': 8.27.0 + + '@sentry-internal/replay-canvas@8.27.0': + dependencies: + '@sentry-internal/replay': 8.27.0 + '@sentry/core': 8.27.0 + '@sentry/types': 8.27.0 + '@sentry/utils': 8.27.0 + + '@sentry-internal/replay@8.27.0': + dependencies: + '@sentry-internal/browser-utils': 8.27.0 + '@sentry/core': 8.27.0 + '@sentry/types': 8.27.0 + '@sentry/utils': 8.27.0 + + '@sentry/browser@8.27.0': + dependencies: + '@sentry-internal/browser-utils': 8.27.0 + '@sentry-internal/feedback': 8.27.0 + '@sentry-internal/replay': 8.27.0 + '@sentry-internal/replay-canvas': 8.27.0 + '@sentry/core': 8.27.0 + '@sentry/types': 8.27.0 + '@sentry/utils': 8.27.0 + + '@sentry/core@8.27.0': + dependencies: + '@sentry/types': 8.27.0 + '@sentry/utils': 8.27.0 + + '@sentry/types@8.27.0': {} + + '@sentry/utils@8.27.0': + dependencies: + '@sentry/types': 8.27.0 + + '@sentry/vue@8.27.0(vue@3.4.31(typescript@5.5.3))': + dependencies: + '@sentry/browser': 8.27.0 + '@sentry/core': 8.27.0 + '@sentry/types': 8.27.0 + '@sentry/utils': 8.27.0 + vue: 3.4.31(typescript@5.5.3) '@sindresorhus/merge-streams@2.3.0': {} @@ -7811,8 +7887,6 @@ snapshots: '@trysound/sax@0.2.0': {} - '@types/css-font-loading-module@0.0.7': {} - '@types/eslint-scope@3.7.7': dependencies: '@types/eslint': 9.6.0 @@ -8475,8 +8549,6 @@ snapshots: '@xtuc/long': 4.2.2 optional: true - '@xstate/fsm@1.6.5': {} - '@xtuc/ieee754@1.2.0': optional: true @@ -8688,8 +8760,6 @@ snapshots: bare-events@2.4.2: optional: true - base64-arraybuffer@1.0.2: {} - base64-js@1.5.1: {} binary-extensions@2.3.0: {} @@ -10583,10 +10653,6 @@ snapshots: mitt@3.0.1: {} - mixpanel-browser@2.53.0: - dependencies: - rrweb: 2.0.0-alpha.13 - mkdirp@1.0.4: {} mlly@1.7.1: @@ -11267,6 +11333,14 @@ snapshots: picocolors: 1.0.1 source-map-js: 1.2.0 + posthog-js@1.158.2: + dependencies: + fflate: 0.4.8 + preact: 10.23.2 + web-vitals: 4.2.3 + + preact@10.23.2: {} + prelude-ls@1.2.1: {} prettier-linter-helpers@1.0.0: @@ -11468,23 +11542,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.18.0 fsevents: 2.3.3 - rrdom@2.0.0-alpha.16: - dependencies: - rrweb-snapshot: 2.0.0-alpha.16 - - rrweb-snapshot@2.0.0-alpha.16: {} - - rrweb@2.0.0-alpha.13: - dependencies: - '@rrweb/types': 2.0.0-alpha.16 - '@types/css-font-loading-module': 0.0.7 - '@xstate/fsm': 1.6.5 - base64-arraybuffer: 1.0.2 - fflate: 0.4.8 - mitt: 3.0.1 - rrdom: 2.0.0-alpha.16 - rrweb-snapshot: 2.0.0-alpha.16 - run-applescript@7.0.0: {} run-parallel@1.2.0: @@ -12455,6 +12512,8 @@ snapshots: graceful-fs: 4.2.11 optional: true + web-vitals@4.2.3: {} + webidl-conversions@3.0.1: {} webpack-sources@3.2.3: {}