From 86b93d70bbc685e685e40f1d673b47a8c1422eb0 Mon Sep 17 00:00:00 2001 From: Ankur Datta <64993082+ankur-arch@users.noreply.github.com> Date: Wed, 12 Aug 2026 11:43:08 +0200 Subject: [PATCH] feat(analytics): count non-consenting visitors with PostHog cookieless mode Since PostHog became consent-gated on CookieYes (#7971, June 22), visitors who reject analytics or never touch the banner disappear from PostHog entirely, which cut measured traffic by roughly 60% and made the top-line visitor metric unusable. This turns on PostHog's cookieless mode so those visitors are counted again without weakening the June consent behavior. - Set cookieless_mode: "on_reject" in docs/site/blog PostHog init. Together with the existing opt_out_capturing_by_default: true, visitors with no consent decision and visitors who rejected analytics are captured cookielessly: events carry the $posthog_cookieless sentinel plus $cookieless_mode: true, and PostHog's servers derive the visitor id from a salted daily hash. Nothing is stored on the device. - Bump posthog-js ^1.351.3 -> ^1.415.7. The installed 1.364.4 predates the SDK change (PostHog/posthog-js#3362, v1.369.4) that makes undecided visitors capture cookielessly, plus several cookieless fixes after it. - Make the CookieYes consent helper tri-state (granted/denied/pending) via isUserActionCompleted so a visitor who merely ignored the banner is not converted into a stored explicit opt-out before making any decision. - Re-register site_name/environment super-properties after consent transitions; the SDK resets its state on opt-in/opt-out in cookieless mode and events after the transition lost those properties otherwise. Requires "Cookieless server hash mode" enabled in PostHog project settings (Project Settings > Web analytics) before deploy, otherwise cookieless events are dropped at ingestion (verified: they currently are). Consent behavior per state, verified against a local build: - no decision yet: cookieless capture, no cookies/localStorage writes - rejected: cookieless capture, only the opt-out flag "0" stored - accepted: full PostHog analytics, unchanged from today - returning visitors: stored decision applies from init Co-Authored-By: Claude Fable 5 --- apps/blog/src/instrumentation-client.ts | 34 ++- apps/docs/src/instrumentation-client.ts | 34 ++- apps/site/src/instrumentation-client.ts | 34 ++- packages/ui/src/lib/consent.ts | 51 ++-- pnpm-lock.yaml | 306 +++++------------------- pnpm-workspace.yaml | 2 +- 6 files changed, 167 insertions(+), 294 deletions(-) diff --git a/apps/blog/src/instrumentation-client.ts b/apps/blog/src/instrumentation-client.ts index 0fd1318f7d..9c82ad2647 100644 --- a/apps/blog/src/instrumentation-client.ts +++ b/apps/blog/src/instrumentation-client.ts @@ -1,25 +1,41 @@ import posthog from "posthog-js"; import { hasAnalyticsConsent, onAnalyticsConsentChange } from "@prisma-docs/ui/lib/consent"; +const SUPER_PROPERTIES = { + site_name: "mono-blog", + environment: "production", +}; + posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST, capture_pageview: "history_change", defaults: "2025-11-30", - // GDPR/ePrivacy: do not set cookies or capture anything until the visitor - // grants analytics consent via CookieYes. Opt-in is handled below. + // GDPR/ePrivacy: no cookies, storage, or persistent identifiers until the + // visitor grants analytics consent via CookieYes. Opt-in is handled below. + // Until then (banner ignored or analytics rejected) visitors are counted + // cookielessly: events carry the $posthog_cookieless sentinel and PostHog's + // servers derive a daily rotating hash; nothing identifying is stored + // on-device. Requires "Cookieless server hash mode" in project settings, + // otherwise these events are dropped at ingestion. + cookieless_mode: "on_reject", + // With cookieless_mode this also makes not-yet-decided visitors count as + // rejected (cookieless) rather than uncaptured. opt_out_capturing_by_default: true, loaded: (posthog) => { - posthog.register({ - site_name: "mono-blog", - environment: "production", - }); + posthog.register(SUPER_PROPERTIES); // Returning visitor whose stored consent is already available at init. if (hasAnalyticsConsent()) posthog.opt_in_capturing(); }, }); // React to live banner interactions and to CookieYes restoring stored consent. -onAnalyticsConsentChange((granted) => { - if (granted) posthog.opt_in_capturing(); - else posthog.opt_out_capturing(); +// "pending" must NOT opt out: an explicit opt-out writes an opt-out flag to +// device storage, and the visitor has not made a decision yet; cookieless +// capture already covers them. +onAnalyticsConsentChange((status) => { + if (status === "granted") posthog.opt_in_capturing(); + else if (status === "denied") posthog.opt_out_capturing(); + // Both transitions reset the SDK state that held the registered + // super-properties, so re-register or later events lose site_name. + if (status !== "pending") posthog.register(SUPER_PROPERTIES); }); diff --git a/apps/docs/src/instrumentation-client.ts b/apps/docs/src/instrumentation-client.ts index 961c59623e..bb42dc97f0 100644 --- a/apps/docs/src/instrumentation-client.ts +++ b/apps/docs/src/instrumentation-client.ts @@ -2,27 +2,43 @@ import posthog from "posthog-js"; import * as Sentry from "@sentry/nextjs"; import { hasAnalyticsConsent, onAnalyticsConsentChange } from "@prisma-docs/ui/lib/consent"; +const SUPER_PROPERTIES = { + site_name: "mono-docs", + environment: "production", +}; + posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST, capture_pageview: "history_change", defaults: "2025-11-30", - // GDPR/ePrivacy: do not set cookies or capture anything until the visitor - // grants analytics consent via CookieYes. Opt-in is handled below. + // GDPR/ePrivacy: no cookies, storage, or persistent identifiers until the + // visitor grants analytics consent via CookieYes. Opt-in is handled below. + // Until then (banner ignored or analytics rejected) visitors are counted + // cookielessly: events carry the $posthog_cookieless sentinel and PostHog's + // servers derive a daily rotating hash; nothing identifying is stored + // on-device. Requires "Cookieless server hash mode" in project settings, + // otherwise these events are dropped at ingestion. + cookieless_mode: "on_reject", + // With cookieless_mode this also makes not-yet-decided visitors count as + // rejected (cookieless) rather than uncaptured. opt_out_capturing_by_default: true, loaded: (posthog) => { - posthog.register({ - site_name: "mono-docs", - environment: "production", - }); + posthog.register(SUPER_PROPERTIES); // Returning visitor whose stored consent is already available at init. if (hasAnalyticsConsent()) posthog.opt_in_capturing(); }, }); // React to live banner interactions and to CookieYes restoring stored consent. -onAnalyticsConsentChange((granted) => { - if (granted) posthog.opt_in_capturing(); - else posthog.opt_out_capturing(); +// "pending" must NOT opt out: an explicit opt-out writes an opt-out flag to +// device storage, and the visitor has not made a decision yet; cookieless +// capture already covers them. +onAnalyticsConsentChange((status) => { + if (status === "granted") posthog.opt_in_capturing(); + else if (status === "denied") posthog.opt_out_capturing(); + // Both transitions reset the SDK state that held the registered + // super-properties, so re-register or later events lose site_name. + if (status !== "pending") posthog.register(SUPER_PROPERTIES); }); Sentry.init({ diff --git a/apps/site/src/instrumentation-client.ts b/apps/site/src/instrumentation-client.ts index 183d314ede..2051d5e76c 100644 --- a/apps/site/src/instrumentation-client.ts +++ b/apps/site/src/instrumentation-client.ts @@ -1,25 +1,41 @@ import posthog from "posthog-js"; import { hasAnalyticsConsent, onAnalyticsConsentChange } from "@prisma-docs/ui/lib/consent"; +const SUPER_PROPERTIES = { + site_name: "mono-site", + environment: "production", +}; + posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST, capture_pageview: "history_change", defaults: "2025-11-30", - // GDPR/ePrivacy: do not set cookies or capture anything until the visitor - // grants analytics consent via CookieYes. Opt-in is handled below. + // GDPR/ePrivacy: no cookies, storage, or persistent identifiers until the + // visitor grants analytics consent via CookieYes. Opt-in is handled below. + // Until then (banner ignored or analytics rejected) visitors are counted + // cookielessly: events carry the $posthog_cookieless sentinel and PostHog's + // servers derive a daily rotating hash; nothing identifying is stored + // on-device. Requires "Cookieless server hash mode" in project settings, + // otherwise these events are dropped at ingestion. + cookieless_mode: "on_reject", + // With cookieless_mode this also makes not-yet-decided visitors count as + // rejected (cookieless) rather than uncaptured. opt_out_capturing_by_default: true, loaded: (posthog) => { - posthog.register({ - site_name: "mono-site", - environment: "production", - }); + posthog.register(SUPER_PROPERTIES); // Returning visitor whose stored consent is already available at init. if (hasAnalyticsConsent()) posthog.opt_in_capturing(); }, }); // React to live banner interactions and to CookieYes restoring stored consent. -onAnalyticsConsentChange((granted) => { - if (granted) posthog.opt_in_capturing(); - else posthog.opt_out_capturing(); +// "pending" must NOT opt out: an explicit opt-out writes an opt-out flag to +// device storage, and the visitor has not made a decision yet; cookieless +// capture already covers them. +onAnalyticsConsentChange((status) => { + if (status === "granted") posthog.opt_in_capturing(); + else if (status === "denied") posthog.opt_out_capturing(); + // Both transitions reset the SDK state that held the registered + // super-properties, so re-register or later events lose site_name. + if (status !== "pending") posthog.register(SUPER_PROPERTIES); }); diff --git a/packages/ui/src/lib/consent.ts b/packages/ui/src/lib/consent.ts index a92e3a4168..fd7be6ad99 100644 --- a/packages/ui/src/lib/consent.ts +++ b/packages/ui/src/lib/consent.ts @@ -9,12 +9,24 @@ * GDPR/ePrivacy note: analytics SDKs must not set cookies or send data until * the visitor grants analytics consent. Callers should start opted-out and * only opt in from these helpers. + * + * Consent is tri-state: a visitor who has never interacted with the banner + * ("pending", `isUserActionCompleted: false`) is not the same as one who + * rejected analytics ("denied"). PostHog's cookieless mode counts pending + * visitors without touching device storage, so callers must not collapse + * "pending" into an explicit opt-out that writes an opt-out flag. */ /** CookieYes category key for analytics cookies. */ const ANALYTICS_CATEGORY = "analytics"; -type CkyConsent = { categories?: Record }; +export type AnalyticsConsentStatus = "granted" | "denied" | "pending"; + +type CkyConsent = { + categories?: Record; + /** True once the visitor has accepted/rejected/saved from the banner. */ + isUserActionCompleted?: boolean; +}; declare global { interface Window { @@ -23,39 +35,48 @@ declare global { } /** - * True when CookieYes has a stored decision granting analytics consent. + * The visitor's stored analytics-consent decision. * - * Returns false during SSR, before CookieYes has loaded, or when the visitor - * has not (yet) accepted analytics — i.e. the safe default is "no consent". + * - `"granted"`: the visitor accepted analytics cookies. + * - `"denied"`: the visitor made a choice that excludes analytics. + * - `"pending"`: SSR, CookieYes not loaded yet, or no banner interaction yet. */ -export function hasAnalyticsConsent(): boolean { - if (typeof window === "undefined") return false; +export function getAnalyticsConsentStatus(): AnalyticsConsentStatus { + if (typeof window === "undefined") return "pending"; try { - return Boolean(window.getCkyConsent?.().categories?.[ANALYTICS_CATEGORY]); + const consent = window.getCkyConsent?.(); + if (!consent || !consent.isUserActionCompleted) return "pending"; + return consent.categories?.[ANALYTICS_CATEGORY] ? "granted" : "denied"; } catch { - return false; + return "pending"; } } +/** True when CookieYes has a stored decision granting analytics consent. */ +export function hasAnalyticsConsent(): boolean { + return getAnalyticsConsentStatus() === "granted"; +} + /** - * Invokes `onChange(granted)` whenever analytics consent changes. + * Invokes `onChange(status)` whenever the analytics-consent status changes. * * - Fires on `cookieyes_consent_update` when the visitor accepts/rejects from - * the banner. - * - Fires on `cookieyes_banner_load` so returning visitors who previously - * consented are opted in once CookieYes restores their stored decision. + * the banner. This is always an explicit decision, so never "pending". + * - Fires on `cookieyes_banner_load` so returning visitors' stored decisions + * are applied once CookieYes restores them. Reports "pending" when the + * visitor has not interacted with the banner yet. * * Safe no-op during SSR. */ -export function onAnalyticsConsentChange(onChange: (granted: boolean) => void): void { +export function onAnalyticsConsentChange(onChange: (status: AnalyticsConsentStatus) => void): void { if (typeof document === "undefined") return; document.addEventListener("cookieyes_consent_update", (event) => { const accepted = (event as CustomEvent<{ accepted?: string[] }>).detail?.accepted ?? []; - onChange(accepted.includes(ANALYTICS_CATEGORY)); + onChange(accepted.includes(ANALYTICS_CATEGORY) ? "granted" : "denied"); }); document.addEventListener("cookieyes_banner_load", () => { - onChange(hasAnalyticsConsent()); + onChange(getAnalyticsConsentStatus()); }); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0ac24cee91..db90e18b6d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -157,8 +157,8 @@ catalogs: specifier: ^8.5.6 version: 8.5.8 posthog-js: - specifier: ^1.351.3 - version: 1.364.4 + specifier: ^1.415.7 + version: 1.415.7 react: specifier: ^19.2.4 version: 19.2.4 @@ -290,7 +290,7 @@ importers: version: 3.0.1 posthog-js: specifier: 'catalog:' - version: 1.364.4 + version: 1.415.7 react: specifier: 'catalog:' version: 19.2.4 @@ -417,7 +417,7 @@ importers: version: 3.0.1 posthog-js: specifier: 'catalog:' - version: 1.364.4 + version: 1.415.7 react: specifier: 'catalog:' version: 19.2.4 @@ -656,7 +656,7 @@ importers: version: 3.0.1 posthog-js: specifier: 'catalog:' - version: 1.364.4 + version: 1.415.7 prop-types: specifier: ^15.8.1 version: 15.8.1 @@ -1815,10 +1815,6 @@ packages: resolution: {integrity: sha512-lAb0jQRVyleQQGiuuvCOTDVspc14nx6XJjP4FspJ1sNARo3Regq4ZZbrc3rN4b1TYSuUCvgH+UXUPug4SLOqEQ==} engines: {node: '>=8.0.0'} - '@opentelemetry/api-logs@0.208.0': - resolution: {integrity: sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==} - engines: {node: '>=8.0.0'} - '@opentelemetry/api-logs@0.212.0': resolution: {integrity: sha512-TEEVrLbNROUkYY51sBJGk7lO/OLjuepch8+hmpM6ffMJQ2z/KVCjdHuCFX6fJj8OkJP2zckPjrJzQtXU3IAsFg==} engines: {node: '>=8.0.0'} @@ -1837,24 +1833,12 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.2.0': - resolution: {integrity: sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.6.1': resolution: {integrity: sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/exporter-logs-otlp-http@0.208.0': - resolution: {integrity: sha512-jOv40Bs9jy9bZVLo/i8FwUiuCvbjWDI+ZW13wimJm4LjnlwJxGgB+N/VWOZUTpM+ah/awXeQqKdNlpLf2EjvYg==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-amqplib@0.61.0': resolution: {integrity: sha512-mCKoyTGfRNisge4br0NpOFSy2Z1NnEW8hbCJdUDdJFHrPqVzc4IIBPA/vX0U+LUcQqrQvJX+HMIU0dbDRe0i0Q==} engines: {node: ^18.19.0 || >=20.6.0} @@ -2005,52 +1989,16 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/otlp-exporter-base@0.208.0': - resolution: {integrity: sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/otlp-transformer@0.208.0': - resolution: {integrity: sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - '@opentelemetry/redis-common@0.38.2': resolution: {integrity: sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA==} engines: {node: ^18.19.0 || >=20.6.0} - '@opentelemetry/resources@2.2.0': - resolution: {integrity: sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/resources@2.6.1': resolution: {integrity: sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-logs@0.208.0': - resolution: {integrity: sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.4.0 <1.10.0' - - '@opentelemetry/sdk-metrics@2.2.0': - resolution: {integrity: sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.9.0 <1.10.0' - - '@opentelemetry/sdk-trace-base@2.2.0': - resolution: {integrity: sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-trace-base@2.6.1': resolution: {integrity: sha512-r86ut4T1e8vNwB35CqCcKd45yzqH6/6Wzvpk2/cZB8PsPLlZFTvrh8yfOS3CYZYcUmAx4hHTZJ8AO8Dj8nrdhw==} engines: {node: ^18.19.0 || >=20.6.0} @@ -2564,47 +2512,20 @@ packages: engines: {node: '>=18'} hasBin: true - '@posthog/core@1.24.4': - resolution: {integrity: sha512-S+TolwBHSSJz7WWtgaELQWQqXviSm3uf1e+qorWUts0bZcgPwWzhnmhCUZAhvn0NVpTQHDJ3epv+hHbPLl5dHg==} + '@posthog/browser-common@0.5.0': + resolution: {integrity: sha512-8DaxVZS1bQPbA514RePurLNbYjei3P4jhnC206DwVv5XThmZM3QdlsXenI2ujE3pLbgQ79hYn9o1Kda8I3WK/Q==} - '@posthog/types@1.364.4': - resolution: {integrity: sha512-U7NpIy9XWrzz1q/66xyDu8Wm12a7avNRKRn5ISPT5kuCJQRaeAaHuf+dpgrFnuqjCCgxg+oIY/ReJdlZ+8/z4Q==} + '@posthog/core@1.47.0': + resolution: {integrity: sha512-LW62V+9yx7G7mLd+EYpwW0PiaPZI8XRJ1tLuhw+9fXHpugLp8XQD5JuCQ2kIXvoUfyx1DpKfGQY3dUnzMOIn7Q==} + + '@posthog/types@1.402.3': + resolution: {integrity: sha512-nnqKIGUqggeNbCZg6of/hYN+4shYZUoPFkILIIpYq0p9HDX8PgOrnrtBAUH8kr1MOmDh2nm+fY5Ceks7A5y6BQ==} '@prisma/instrumentation@7.6.0': resolution: {integrity: sha512-ZPW2gRiwpPzEfgeZgaekhqXrbW+Y2RJKHVqUmlhZhKzRNCcvR6DykzylDrynpArKKRQtLxoZy36fK7U0p3pdgQ==} peerDependencies: '@opentelemetry/api': ^1.8 - '@protobufjs/aspromise@1.1.2': - resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} - - '@protobufjs/base64@1.1.2': - resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} - - '@protobufjs/codegen@2.0.4': - resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} - - '@protobufjs/eventemitter@1.1.0': - resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} - - '@protobufjs/fetch@1.1.0': - resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} - - '@protobufjs/float@1.0.2': - resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} - - '@protobufjs/inquire@1.1.0': - resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} - - '@protobufjs/path@1.1.2': - resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} - - '@protobufjs/pool@1.1.0': - resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} - - '@protobufjs/utf8@1.1.0': - resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} - '@quansync/fs@1.0.0': resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} @@ -4323,10 +4244,6 @@ packages: cose-base@2.2.0: resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - cspell-config-lib@9.7.0: resolution: {integrity: sha512-pguh8A3+bSJ1OOrKCiQan8bvaaY125de76OEFz7q1Pq309lIcDrkoL/W4aYbso/NjrXaIw6OjkgPMGRBI/IgGg==} engines: {node: '>=20'} @@ -4599,6 +4516,9 @@ packages: dompurify@3.3.3: resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==} + dompurify@3.4.13: + resolution: {integrity: sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==} + domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} @@ -5378,9 +5298,6 @@ packages: lodash@4.17.23: resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} - long@5.3.2: - resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} - longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -5874,10 +5791,6 @@ packages: resolution: {integrity: sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==} engines: {node: '>=14.0.0'} - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - path-scurry@2.0.2: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} @@ -5957,8 +5870,8 @@ packages: resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} engines: {node: '>=0.10.0'} - posthog-js@1.364.4: - resolution: {integrity: sha512-T71zr06gH5YcrjS7c+sdzqfZKMxqqXC/a0w++zMQIPbL1ejvF9PdfUi0Kyd6Sy78Ocbb2smobdzBh8vXLwC+lQ==} + posthog-js@1.415.7: + resolution: {integrity: sha512-wczdBPDh6vr/Knft0HaJ+naO5NXVaML9Zk9ufoeejO16WrA0TeiX3yqv160eoqWVnWOU5tniYs8hMcxMLmpz2Q==} postman-collection@5.3.0: resolution: {integrity: sha512-PMa5vRheqDFfS1bkRg8WBidWxunRA80sT5YNLP27YC5+ycyfiLMCwPnqQd1zfvxkGk04Pr9UronWmmgsbpsVyQ==} @@ -5968,8 +5881,13 @@ packages: resolution: {integrity: sha512-EOgUMBazo7JNP4TDrd64TsooCiWzzo4143Ws8E8WYGEpn2PKpq+S4XRTDhuRTYHm3VKOpUZs7ZYZq7zSDuesqA==} engines: {node: '>=10'} - preact@10.29.0: - resolution: {integrity: sha512-wSAGyk2bYR1c7t3SZ3jHcM6xy0lcBcDel6lODcs9ME6Th++Dx2KU+6D3HD8wMMKGA8Wpw7OMd3/4RGzYRpzwRg==} + preact@10.29.8: + resolution: {integrity: sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==} + peerDependencies: + preact-render-to-string: '>=5' + peerDependenciesMeta: + preact-render-to-string: + optional: true progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} @@ -5981,10 +5899,6 @@ packages: property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} - protobufjs@7.5.4: - resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} - engines: {node: '>=12.0.0'} - proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -6307,14 +6221,6 @@ packages: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - shiki@4.0.2: resolution: {integrity: sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ==} engines: {node: '>=20'} @@ -6725,8 +6631,11 @@ packages: web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} - web-vitals@5.2.0: - resolution: {integrity: sha512-i2z98bEmaCqSDiHEDu+gHl/dmR4Q+TxFmG3/13KkMO+o8UxQzCqWaDRCiLgEa41nlO4VpXSI0ASa1xWmO9sBlA==} + web-vitals@5.3.0: + resolution: {integrity: sha512-q6LWsLatGYZp5VGBIOvbTj6JBV2nOmC8KvWztXBmwJcfFAzhwKwbOxhUH306XY3CcaZDUlSmSuNPBsCn0bFu+g==} + + web-vitals@6.0.0: + resolution: {integrity: sha512-Guaibvy/+uNtL6Bsu4jmMJGzuSl91oeRH5iO9pPRbYftnFUr3yqT1TUNX/OE4o9HexuEMU3Kb/Wg7iKhlffZUA==} webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -7706,10 +7615,6 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs@0.208.0': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs@0.212.0': dependencies: '@opentelemetry/api': 1.9.1 @@ -7724,25 +7629,11 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/exporter-logs-otlp-http@0.208.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.208.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-exporter-base': 0.208.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-amqplib@0.61.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -7961,57 +7852,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/otlp-exporter-base@0.208.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.1) - - '@opentelemetry/otlp-transformer@0.208.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.208.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-metrics': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.1) - protobufjs: 7.5.4 - '@opentelemetry/redis-common@0.38.2': {} - '@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/resources@2.6.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/sdk-logs@0.208.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.208.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.1) - - '@opentelemetry/sdk-metrics@2.2.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.1) - - '@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -8282,11 +8130,16 @@ snapshots: dependencies: playwright: 1.59.0 - '@posthog/core@1.24.4': + '@posthog/browser-common@0.5.0': + dependencies: + '@posthog/core': 1.47.0 + '@posthog/types': 1.402.3 + + '@posthog/core@1.47.0': dependencies: - cross-spawn: 7.0.6 + '@posthog/types': 1.402.3 - '@posthog/types@1.364.4': {} + '@posthog/types@1.402.3': {} '@prisma/instrumentation@7.6.0(@opentelemetry/api@1.9.1)': dependencies: @@ -8295,29 +8148,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@protobufjs/aspromise@1.1.2': {} - - '@protobufjs/base64@1.1.2': {} - - '@protobufjs/codegen@2.0.4': {} - - '@protobufjs/eventemitter@1.1.0': {} - - '@protobufjs/fetch@1.1.0': - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/inquire': 1.1.0 - - '@protobufjs/float@1.0.2': {} - - '@protobufjs/inquire@1.1.0': {} - - '@protobufjs/path@1.1.2': {} - - '@protobufjs/pool@1.1.0': {} - - '@protobufjs/utf8@1.1.0': {} - '@quansync/fs@1.0.0': dependencies: quansync: 1.0.0 @@ -9968,12 +9798,6 @@ snapshots: dependencies: layout-base: 2.0.1 - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - cspell-config-lib@9.7.0: dependencies: '@cspell/cspell-types': 9.7.0 @@ -10308,6 +10132,10 @@ snapshots: optionalDependencies: '@types/trusted-types': 2.0.7 + dompurify@3.4.13: + optionalDependencies: + '@types/trusted-types': 2.0.7 + domutils@3.2.2: dependencies: dom-serializer: 2.0.0 @@ -11092,8 +10920,6 @@ snapshots: lodash@4.17.23: {} - long@5.3.2: {} - longest-streak@3.1.0: {} loose-envify@1.4.0: @@ -11980,8 +11806,6 @@ snapshots: path-expression-matcher@1.2.0: {} - path-key@3.1.1: {} - path-scurry@2.0.2: dependencies: lru-cache: 11.2.7 @@ -12057,21 +11881,20 @@ snapshots: dependencies: xtend: 4.0.2 - posthog-js@1.364.4: + posthog-js@1.415.7: dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.208.0 - '@opentelemetry/exporter-logs-otlp-http': 0.208.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.1) - '@posthog/core': 1.24.4 - '@posthog/types': 1.364.4 + '@posthog/browser-common': 0.5.0 + '@posthog/core': 1.47.0 + '@posthog/types': 1.402.3 core-js: 3.49.0 - dompurify: 3.3.3 + dompurify: 3.4.13 fflate: 0.4.8 - preact: 10.29.0 + preact: 10.29.8 query-selector-shadow-dom: 1.0.1 - web-vitals: 5.2.0 + web-vitals: 5.3.0 + web-vitals-soft-navs: web-vitals@6.0.0 + transitivePeerDependencies: + - preact-render-to-string postman-collection@5.3.0: dependencies: @@ -12091,7 +11914,7 @@ snapshots: dependencies: punycode: 2.3.1 - preact@10.29.0: {} + preact@10.29.8: {} progress@2.0.3: {} @@ -12103,21 +11926,6 @@ snapshots: property-information@7.1.0: {} - protobufjs@7.5.4: - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/base64': 1.1.2 - '@protobufjs/codegen': 2.0.4 - '@protobufjs/eventemitter': 1.1.0 - '@protobufjs/fetch': 1.1.0 - '@protobufjs/float': 1.0.2 - '@protobufjs/inquire': 1.1.0 - '@protobufjs/path': 1.1.2 - '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.0 - '@types/node': 25.5.0 - long: 5.3.2 - proxy-from-env@1.1.0: {} punycode@2.3.1: {} @@ -12564,12 +12372,6 @@ snapshots: '@img/sharp-win32-ia32': 0.34.5 '@img/sharp-win32-x64': 0.34.5 - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - shiki@4.0.2: dependencies: '@shikijs/core': 4.0.2 @@ -13007,7 +12809,9 @@ snapshots: web-namespaces@2.0.1: {} - web-vitals@5.2.0: {} + web-vitals@5.3.0: {} + + web-vitals@6.0.0: {} webidl-conversions@3.0.1: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 153403ef73..e83187e299 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -52,7 +52,7 @@ catalog: "oxfmt": "^0.46.0" "oxlint": "^1.61.0" "postcss": "^8.5.6" - "posthog-js": "^1.351.3" + "posthog-js": "^1.415.7" "react": "^19.2.4" "react-animated-numbers": "^1.1.1" "react-dom": "^19.2.4"