feat: add client-side PostHog telemetry tracking (#37442)
Adds client-side PostHog tracking to run in parallel with server-side telemetry across studio, docs, and www. This enables session replays and resolves a race condition where page views arrive before group assignments resulting in attribution errors. Changes: - Created PostHog client wrapper with consent-aware initialization in common package - Integrated PostHog client calls into existing telemetry functions to send events to both PostHog (client) and backend (server) - Updated CSP to allow connections to PostHog endpoints - Added environment variable support for all apps - PostHog client accepts consent as a parameter and respects user preferences - Events can be distinguished in PostHog by $lib property (posthog-js vs posthog-node) - PostHog URL configured based on environment (staging/local uses ph.supabase.green) - Maintains full backward compatibility with existing telemetry system Resolves GROWTH-438 Resolves GROWTH-271
This commit is contained in:
@@ -26,6 +26,11 @@ const SUPABASE_CONTENT_API_URL = process.env.NEXT_PUBLIC_CONTENT_API_URL
|
||||
? new URL(process.env.NEXT_PUBLIC_CONTENT_API_URL).origin
|
||||
: ''
|
||||
|
||||
const isDevOrStaging =
|
||||
process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview' ||
|
||||
process.env.NEXT_PUBLIC_ENVIRONMENT === 'local' ||
|
||||
process.env.NEXT_PUBLIC_ENVIRONMENT === 'staging'
|
||||
|
||||
const SUPABASE_STAGING_PROJECTS_URL = 'https://*.supabase.red'
|
||||
const SUPABASE_STAGING_PROJECTS_URL_WS = 'wss://*.supabase.red'
|
||||
const SUPABASE_COM_URL = 'https://supabase.com'
|
||||
@@ -58,6 +63,7 @@ const SUPABASE_ASSETS_URL =
|
||||
process.env.NEXT_PUBLIC_ENVIRONMENT === 'staging'
|
||||
? 'https://frontend-assets.supabase.green'
|
||||
: 'https://frontend-assets.supabase.com'
|
||||
const POSTHOG_URL = isDevOrStaging ? 'https://ph.supabase.green' : 'https://ph.supabase.com'
|
||||
|
||||
const USERCENTRICS_URLS = 'https://*.usercentrics.eu'
|
||||
const USERCENTRICS_APP_URL = 'https://app.usercentrics.eu'
|
||||
@@ -89,6 +95,7 @@ module.exports.getCSP = function getCSP() {
|
||||
USERCENTRICS_URLS,
|
||||
STAPE_URL,
|
||||
GOOGLE_MAPS_API_URL,
|
||||
POSTHOG_URL,
|
||||
]
|
||||
const SCRIPT_SRC_URLS = [
|
||||
CLOUDFLARE_CDN_URL,
|
||||
@@ -96,6 +103,7 @@ module.exports.getCSP = function getCSP() {
|
||||
STRIPE_JS_URL,
|
||||
SUPABASE_ASSETS_URL,
|
||||
STAPE_URL,
|
||||
POSTHOG_URL,
|
||||
]
|
||||
const FRAME_SRC_URLS = [HCAPTCHA_ASSET_URL, STRIPE_JS_URL, STAPE_URL]
|
||||
const IMG_SRC_URLS = [
|
||||
@@ -111,11 +119,6 @@ module.exports.getCSP = function getCSP() {
|
||||
const STYLE_SRC_URLS = [CLOUDFLARE_CDN_URL, SUPABASE_ASSETS_URL]
|
||||
const FONT_SRC_URLS = [CLOUDFLARE_CDN_URL, SUPABASE_ASSETS_URL]
|
||||
|
||||
const isDevOrStaging =
|
||||
process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview' ||
|
||||
process.env.NEXT_PUBLIC_ENVIRONMENT === 'local' ||
|
||||
process.env.NEXT_PUBLIC_ENVIRONMENT === 'staging'
|
||||
|
||||
const defaultSrcDirective = [
|
||||
`default-src 'self'`,
|
||||
...DEFAULT_SRC_URLS,
|
||||
|
||||
@@ -37,6 +37,12 @@ export const GOTRUE_ERRORS = {
|
||||
export const STRIPE_PUBLIC_KEY =
|
||||
process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY || 'pk_test_XVwg5IZH3I9Gti98hZw6KRzd00v5858heG'
|
||||
|
||||
export const POSTHOG_URL =
|
||||
process.env.NEXT_PUBLIC_ENVIRONMENT === 'staging' ||
|
||||
process.env.NEXT_PUBLIC_ENVIRONMENT === 'local'
|
||||
? 'https://ph.supabase.green'
|
||||
: 'https://ph.supabase.com'
|
||||
|
||||
export const USAGE_APPROACHING_THRESHOLD = 0.75
|
||||
|
||||
export const OPT_IN_TAGS = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { PageTelemetry } from 'common'
|
||||
import GroupsTelemetry from 'components/ui/GroupsTelemetry'
|
||||
import { API_URL, IS_PLATFORM } from 'lib/constants'
|
||||
import { useConsentToast } from 'ui-patterns/consent'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
|
||||
export function Telemetry() {
|
||||
// Although this is "technically" breaking the rules of hooks
|
||||
@@ -9,14 +9,16 @@ export function Telemetry() {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const { hasAcceptedConsent } = IS_PLATFORM ? useConsentToast() : { hasAcceptedConsent: true }
|
||||
|
||||
// Get org from selected organization query because it's not
|
||||
// always available in the URL params
|
||||
const { data: organization } = useSelectedOrganizationQuery()
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTelemetry
|
||||
API_URL={API_URL}
|
||||
hasAcceptedConsent={hasAcceptedConsent}
|
||||
enabled={IS_PLATFORM}
|
||||
/>
|
||||
<GroupsTelemetry hasAcceptedConsent={hasAcceptedConsent} />
|
||||
</>
|
||||
<PageTelemetry
|
||||
API_URL={API_URL}
|
||||
hasAcceptedConsent={hasAcceptedConsent}
|
||||
enabled={IS_PLATFORM}
|
||||
organizationSlug={organization?.slug}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,12 +13,13 @@
|
||||
"dependencies": {
|
||||
"@types/dat.gui": "^0.7.12",
|
||||
"@usercentrics/cmp-browser-sdk": "^4.42.0",
|
||||
"flags": "^4.0.0",
|
||||
"api-types": "workspace:*",
|
||||
"config": "workspace:*",
|
||||
"dat.gui": "^0.7.9",
|
||||
"flags": "^4.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"next-themes": "^0.3.0",
|
||||
"posthog-js": "^1.257.2",
|
||||
"react-use": "^17.4.0",
|
||||
"valtio": "catalog:"
|
||||
},
|
||||
|
||||
65
packages/common/posthog-client.ts
Normal file
65
packages/common/posthog-client.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import posthog from 'posthog-js'
|
||||
import { PostHogConfig } from 'posthog-js'
|
||||
|
||||
interface PostHogClientConfig {
|
||||
apiKey?: string
|
||||
apiHost?: string
|
||||
}
|
||||
|
||||
class PostHogClient {
|
||||
private initialized = false
|
||||
private pendingGroups: Record<string, string> = {}
|
||||
private config: PostHogClientConfig
|
||||
|
||||
constructor(config: PostHogClientConfig = {}) {
|
||||
this.config = {
|
||||
apiKey: config.apiKey || process.env.NEXT_PUBLIC_POSTHOG_KEY,
|
||||
apiHost:
|
||||
config.apiHost || process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://ph.supabase.green',
|
||||
}
|
||||
}
|
||||
|
||||
init(hasConsent: boolean = true) {
|
||||
if (this.initialized || typeof window === 'undefined' || !hasConsent) return
|
||||
|
||||
if (!this.config.apiKey) {
|
||||
console.warn('PostHog API key not found. Skipping initialization.')
|
||||
return
|
||||
}
|
||||
|
||||
const config: Partial<PostHogConfig> = {
|
||||
api_host: this.config.apiHost,
|
||||
autocapture: false, // We'll manually track events
|
||||
capture_pageview: false, // We'll manually track pageviews
|
||||
capture_pageleave: false, // We'll manually track page leaves
|
||||
loaded: (posthog) => {
|
||||
// Apply any pending groups
|
||||
Object.entries(this.pendingGroups).forEach(([type, id]) => {
|
||||
posthog.group(type, id)
|
||||
})
|
||||
this.pendingGroups = {}
|
||||
},
|
||||
}
|
||||
|
||||
posthog.init(this.config.apiKey, config)
|
||||
this.initialized = true
|
||||
}
|
||||
|
||||
capturePageView(properties: Record<string, any>, hasConsent: boolean = true) {
|
||||
if (!hasConsent || !this.initialized) return
|
||||
posthog.capture('$pageview', properties)
|
||||
}
|
||||
|
||||
capturePageLeave(properties: Record<string, any>, hasConsent: boolean = true) {
|
||||
if (!hasConsent || !this.initialized) return
|
||||
posthog.capture('$pageleave', properties)
|
||||
}
|
||||
|
||||
identify(userId: string, properties?: Record<string, any>, hasConsent: boolean = true) {
|
||||
if (!hasConsent || !this.initialized) return
|
||||
|
||||
posthog.identify(userId, properties)
|
||||
}
|
||||
}
|
||||
|
||||
export const posthogClient = new PostHogClient()
|
||||
@@ -12,9 +12,10 @@ import { IS_PLATFORM, LOCAL_STORAGE_KEYS } from './constants'
|
||||
import { useFeatureFlags } from './feature-flags'
|
||||
import { post } from './fetchWrappers'
|
||||
import { ensurePlatformSuffix, isBrowser } from './helpers'
|
||||
import { useTelemetryCookie } from './hooks'
|
||||
import { useParams, useTelemetryCookie } from './hooks'
|
||||
import { TelemetryEvent } from './telemetry-constants'
|
||||
import { getSharedTelemetryData } from './telemetry-utils'
|
||||
import { posthogClient } from './posthog-client'
|
||||
|
||||
const { TELEMETRY_DATA } = LOCAL_STORAGE_KEYS
|
||||
|
||||
@@ -46,14 +47,45 @@ export function handlePageTelemetry(
|
||||
featureFlags?: {
|
||||
[key: string]: unknown
|
||||
},
|
||||
slug?: string,
|
||||
ref?: string,
|
||||
telemetryDataOverride?: components['schemas']['TelemetryPageBodyV2']
|
||||
) {
|
||||
// Send to PostHog client-side (only in browser)
|
||||
if (typeof window !== 'undefined') {
|
||||
const pageData = getSharedTelemetryData(pathname)
|
||||
posthogClient.capturePageView({
|
||||
$current_url: pageData.page_url,
|
||||
$pathname: pageData.pathname,
|
||||
$host: new URL(pageData.page_url).hostname,
|
||||
$groups: {
|
||||
...(slug ? { organization: slug } : {}),
|
||||
...(ref ? { project: ref } : {}),
|
||||
},
|
||||
page_title: pageData.page_title,
|
||||
...pageData.ph,
|
||||
...Object.fromEntries(
|
||||
Object.entries(featureFlags || {}).map(([k, v]) => [`$feature/${k}`, v])
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
// Send to backend
|
||||
// TODO: Remove this once migration to client-side page telemetry is complete
|
||||
return post(
|
||||
`${ensurePlatformSuffix(API_URL)}/telemetry/page`,
|
||||
telemetryDataOverride !== undefined
|
||||
? { feature_flags: featureFlags, ...telemetryDataOverride }
|
||||
: {
|
||||
...getSharedTelemetryData(pathname),
|
||||
...(slug || ref
|
||||
? {
|
||||
groups: {
|
||||
...(slug ? { organization: slug } : {}),
|
||||
...(ref ? { project: ref } : {}),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
feature_flags: featureFlags,
|
||||
},
|
||||
{ headers: { Version: '2' } }
|
||||
@@ -65,15 +97,35 @@ export function handlePageLeaveTelemetry(
|
||||
pathname: string,
|
||||
featureFlags?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
},
|
||||
slug?: string,
|
||||
ref?: string
|
||||
) {
|
||||
// Send to PostHog client-side (only in browser)
|
||||
if (typeof window !== 'undefined') {
|
||||
const pageData = getSharedTelemetryData(pathname)
|
||||
posthogClient.capturePageLeave({
|
||||
$current_url: pageData.page_url,
|
||||
$pathname: pageData.pathname,
|
||||
page_title: pageData.page_title,
|
||||
})
|
||||
}
|
||||
|
||||
// Send to backend
|
||||
// TODO: Remove this once migration to client-side page telemetry is complete
|
||||
return post(`${ensurePlatformSuffix(API_URL)}/telemetry/page-leave`, {
|
||||
body: {
|
||||
pathname,
|
||||
page_url: isBrowser ? window.location.href : '',
|
||||
page_title: isBrowser ? document?.title : '',
|
||||
feature_flags: featureFlags,
|
||||
},
|
||||
pathname,
|
||||
page_url: isBrowser ? window.location.href : '',
|
||||
page_title: isBrowser ? document?.title : '',
|
||||
feature_flags: featureFlags,
|
||||
...(slug || ref
|
||||
? {
|
||||
groups: {
|
||||
...(slug ? { organization: slug } : {}),
|
||||
...(ref ? { project: ref } : {}),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -81,16 +133,25 @@ export const PageTelemetry = ({
|
||||
API_URL,
|
||||
hasAcceptedConsent,
|
||||
enabled = true,
|
||||
organizationSlug,
|
||||
projectRef,
|
||||
}: {
|
||||
API_URL: string
|
||||
hasAcceptedConsent: boolean
|
||||
enabled?: boolean
|
||||
organizationSlug?: string
|
||||
projectRef?: string
|
||||
}) => {
|
||||
const router = useRouter()
|
||||
|
||||
const pagesPathname = router?.pathname
|
||||
const appPathname = usePathname()
|
||||
|
||||
// Get from props or try to extract from URL params
|
||||
const params = useParams()
|
||||
const slug = organizationSlug || params.slug
|
||||
const ref = projectRef || params.ref
|
||||
|
||||
const featureFlags = useFeatureFlags()
|
||||
|
||||
const title = typeof document !== 'undefined' ? document?.title : ''
|
||||
@@ -105,21 +166,43 @@ export const PageTelemetry = ({
|
||||
const sendPageTelemetry = useCallback(() => {
|
||||
if (!(enabled && hasAcceptedConsent)) return Promise.resolve()
|
||||
|
||||
return handlePageTelemetry(API_URL, pathnameRef.current, featureFlagsRef.current).catch((e) => {
|
||||
return handlePageTelemetry(
|
||||
API_URL,
|
||||
pathnameRef.current,
|
||||
featureFlagsRef.current,
|
||||
slug,
|
||||
ref
|
||||
).catch((e) => {
|
||||
console.error('Problem sending telemetry page:', e)
|
||||
})
|
||||
}, [API_URL, enabled, hasAcceptedConsent])
|
||||
}, [API_URL, enabled, hasAcceptedConsent, slug, ref])
|
||||
|
||||
const sendPageLeaveTelemetry = useCallback(() => {
|
||||
if (!(enabled && hasAcceptedConsent)) return Promise.resolve()
|
||||
|
||||
return handlePageTelemetry(API_URL, pathnameRef.current, featureFlagsRef.current).catch((e) => {
|
||||
if (!pathnameRef.current) return Promise.resolve()
|
||||
|
||||
return handlePageLeaveTelemetry(
|
||||
API_URL,
|
||||
pathnameRef.current,
|
||||
featureFlagsRef.current,
|
||||
slug,
|
||||
ref
|
||||
).catch((e) => {
|
||||
console.error('Problem sending telemetry page-leave:', e)
|
||||
})
|
||||
}, [API_URL, enabled, hasAcceptedConsent])
|
||||
}, [API_URL, enabled, hasAcceptedConsent, slug, ref])
|
||||
|
||||
// Handle initial page telemetry event
|
||||
const hasSentInitialPageTelemetryRef = useRef(false)
|
||||
|
||||
// Initialize PostHog client when consent is accepted
|
||||
useEffect(() => {
|
||||
if (hasAcceptedConsent && IS_PLATFORM) {
|
||||
posthogClient.init(true)
|
||||
}
|
||||
}, [hasAcceptedConsent, IS_PLATFORM])
|
||||
|
||||
useEffect(() => {
|
||||
// Send page telemetry on first page load
|
||||
// Waiting for router ready before sending page_view
|
||||
@@ -136,19 +219,26 @@ export const PageTelemetry = ({
|
||||
try {
|
||||
const encodedData = telemetryCookie.split('=')[1]
|
||||
const telemetryData = JSON.parse(decodeURIComponent(encodedData))
|
||||
handlePageTelemetry(API_URL, pathnameRef.current, featureFlagsRef.current, telemetryData)
|
||||
handlePageTelemetry(
|
||||
API_URL,
|
||||
pathnameRef.current,
|
||||
featureFlagsRef.current,
|
||||
slug,
|
||||
ref,
|
||||
telemetryData
|
||||
)
|
||||
// remove the telemetry cookie
|
||||
document.cookie = `${TELEMETRY_DATA}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`
|
||||
} catch (error) {
|
||||
console.error('Invalid telemetry data:', error)
|
||||
}
|
||||
} else {
|
||||
handlePageTelemetry(API_URL, pathnameRef.current, featureFlagsRef.current)
|
||||
handlePageTelemetry(API_URL, pathnameRef.current, featureFlagsRef.current, slug, ref)
|
||||
}
|
||||
|
||||
hasSentInitialPageTelemetryRef.current = true
|
||||
}
|
||||
}, [router?.isReady, hasAcceptedConsent, featureFlags.hasLoaded])
|
||||
}, [router?.isReady, hasAcceptedConsent, featureFlags.hasLoaded, slug, ref])
|
||||
|
||||
useEffect(() => {
|
||||
// For pages router
|
||||
@@ -244,9 +334,13 @@ export function useTelemetryIdentify(API_URL: string) {
|
||||
|
||||
useEffect(() => {
|
||||
if (user?.id) {
|
||||
// Send to backend
|
||||
sendTelemetryIdentify(API_URL, {
|
||||
user_id: user.id,
|
||||
})
|
||||
|
||||
// Also identify in PostHog client-side
|
||||
posthogClient.identify(user.id)
|
||||
}
|
||||
}, [API_URL, user?.id])
|
||||
}
|
||||
|
||||
42
pnpm-lock.yaml
generated
42
pnpm-lock.yaml
generated
@@ -1853,6 +1853,9 @@ importers:
|
||||
next-themes:
|
||||
specifier: ^0.3.0
|
||||
version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
posthog-js:
|
||||
specifier: ^1.257.2
|
||||
version: 1.257.2
|
||||
react:
|
||||
specifier: 'catalog:'
|
||||
version: 18.3.1
|
||||
@@ -10275,6 +10278,9 @@ packages:
|
||||
core-js@3.35.0:
|
||||
resolution: {integrity: sha512-ntakECeqg81KqMueeGJ79Q5ZgQNR+6eaE8sxGCx62zMbAIj65q+uYvatToew3m6eAGdU4gNZwpZ34NMe4GYswg==}
|
||||
|
||||
core-js@3.44.0:
|
||||
resolution: {integrity: sha512-aFCtd4l6GvAXwVEh3XbbVqJGHDJt0OZRa+5ePGx3LLwi12WfexqQxcsohb2wgsa/92xtl19Hd66G/L+TaAxDMw==}
|
||||
|
||||
core-util-is@1.0.2:
|
||||
resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
|
||||
|
||||
@@ -11316,7 +11322,6 @@ packages:
|
||||
resolution: {integrity: sha512-t0q23FIpvHDTtnORW+bDJziGsal5uh9RJTJ1fyH8drd4lICOoXhJ5pLMUZ5C0VQei6dNmwTzzoTRgMkO9JgHEQ==}
|
||||
peerDependencies:
|
||||
eslint: '>= 5'
|
||||
bundledDependencies: []
|
||||
|
||||
eslint-plugin-import@2.31.0:
|
||||
resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==}
|
||||
@@ -11674,6 +11679,9 @@ packages:
|
||||
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
|
||||
engines: {node: ^12.20 || >= 14.13}
|
||||
|
||||
fflate@0.4.8:
|
||||
resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
|
||||
|
||||
fflate@0.7.4:
|
||||
resolution: {integrity: sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==}
|
||||
|
||||
@@ -15209,6 +15217,20 @@ packages:
|
||||
postgres-range@1.1.4:
|
||||
resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==}
|
||||
|
||||
posthog-js@1.257.2:
|
||||
resolution: {integrity: sha512-E+8wI/ahaiUGrmkilOtAB9aTFL+oELwOEsH1eO/2NyXB5WWcSUk6Rm1loixq8/lC4f3oR+Qqp9rHyXTSYbBDRQ==}
|
||||
peerDependencies:
|
||||
'@rrweb/types': 2.0.0-alpha.17
|
||||
rrweb-snapshot: 2.0.0-alpha.17
|
||||
peerDependenciesMeta:
|
||||
'@rrweb/types':
|
||||
optional: true
|
||||
rrweb-snapshot:
|
||||
optional: true
|
||||
|
||||
preact@10.26.9:
|
||||
resolution: {integrity: sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA==}
|
||||
|
||||
prebuild-install@7.1.3:
|
||||
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -18077,6 +18099,9 @@ packages:
|
||||
resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
web-vitals@4.2.4:
|
||||
resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==}
|
||||
|
||||
webidl-conversions@3.0.1:
|
||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||
|
||||
@@ -29534,6 +29559,8 @@ snapshots:
|
||||
|
||||
core-js@3.35.0: {}
|
||||
|
||||
core-js@3.44.0: {}
|
||||
|
||||
core-util-is@1.0.2: {}
|
||||
|
||||
core-util-is@1.0.3: {}
|
||||
@@ -31044,6 +31071,8 @@ snapshots:
|
||||
node-domexception: 1.0.0
|
||||
web-streams-polyfill: 3.2.1
|
||||
|
||||
fflate@0.4.8: {}
|
||||
|
||||
fflate@0.7.4: {}
|
||||
|
||||
fflate@0.8.2: {}
|
||||
@@ -35723,6 +35752,15 @@ snapshots:
|
||||
|
||||
postgres-range@1.1.4: {}
|
||||
|
||||
posthog-js@1.257.2:
|
||||
dependencies:
|
||||
core-js: 3.44.0
|
||||
fflate: 0.4.8
|
||||
preact: 10.26.9
|
||||
web-vitals: 4.2.4
|
||||
|
||||
preact@10.26.9: {}
|
||||
|
||||
prebuild-install@7.1.3:
|
||||
dependencies:
|
||||
detect-libc: 2.0.3
|
||||
@@ -39383,6 +39421,8 @@ snapshots:
|
||||
|
||||
web-streams-polyfill@4.0.0-beta.3: {}
|
||||
|
||||
web-vitals@4.2.4: {}
|
||||
|
||||
webidl-conversions@3.0.1: {}
|
||||
|
||||
webidl-conversions@7.0.0:
|
||||
|
||||
Reference in New Issue
Block a user