@convex-internal/web-analytics 1.0.3 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -7
- package/dist/WebAnalyticsProvider.d.ts +6 -11
- package/dist/WebAnalyticsProvider.js +34 -27
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,14 +14,13 @@ provider.
|
|
|
14
14
|
|
|
15
15
|
The provider starts analytics in production, except on localhost, syncs
|
|
16
16
|
consent to PostHog, and shows the consent banner when the visitor has not
|
|
17
|
-
chosen yet.
|
|
18
|
-
website's `dub_id` campaign param:
|
|
17
|
+
chosen yet.
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
PostHog already copies standard campaign params (UTMs, `gclid`, `rdt_cid`,
|
|
20
|
+
`fbclid`, and other common click IDs) from the landing URL onto events. This
|
|
21
|
+
package also captures `dub_id`. After consent, PostHog creates an anonymous
|
|
22
|
+
person profile. Campaign params still available at that point can be stored as
|
|
23
|
+
`$initial_*`, supporting person-level attribution before login.
|
|
25
24
|
|
|
26
25
|
### `capture`
|
|
27
26
|
|
|
@@ -1,22 +1,17 @@
|
|
|
1
|
-
import { type PostHogConfig } from "posthog-js";
|
|
2
1
|
import { type ReactNode } from "react";
|
|
3
|
-
export
|
|
2
|
+
export declare const POSTHOG_PUBLIC_PROJECT_TOKEN = "phc_JDNTRxeh2li2sQTRO0IcOYMJcp8fPs5nTK9TU751nQK";
|
|
4
3
|
/**
|
|
5
|
-
* `"unknown"` until the cookie has been read
|
|
6
|
-
*
|
|
7
|
-
* when the cookie is `true` and Global Privacy Control
|
|
8
|
-
* `"declined"` when the cookie is `false`, or while GPC is
|
|
9
|
-
*
|
|
10
|
-
* GPC does not write or change the cookie. If it is later turned off, the
|
|
11
|
-
* stored cookie (or the banner, if none) applies again.
|
|
4
|
+
* `"unknown"` until the cookie has been read (SSR and first client render).
|
|
5
|
+
* `"undecided"` when it has been read and there is no recognized choice.
|
|
6
|
+
* `"consented"` when the cookie is `true` and Global Privacy Control is off.
|
|
7
|
+
* `"declined"` when the cookie is `false`, or while GPC is on.
|
|
12
8
|
*/
|
|
13
9
|
export type ConsentStatus = "unknown" | "undecided" | "consented" | "declined";
|
|
14
10
|
export interface WebAnalyticsProviderProps {
|
|
15
11
|
children: ReactNode;
|
|
16
|
-
posthogConfig?: Partial<PostHogConfig>;
|
|
17
12
|
}
|
|
18
13
|
export declare const ConsentContext: import("react").Context<{
|
|
19
14
|
consent: ConsentStatus;
|
|
20
15
|
setConsent: (status: "consented" | "declined") => void;
|
|
21
16
|
} | null>;
|
|
22
|
-
export declare function WebAnalyticsProvider({ children
|
|
17
|
+
export declare function WebAnalyticsProvider({ children }: WebAnalyticsProviderProps): import("react").JSX.Element;
|
|
@@ -4,9 +4,15 @@ import { posthog } from "posthog-js";
|
|
|
4
4
|
import { createContext, useEffect } from "react";
|
|
5
5
|
import { ConsentBanner } from "./ConsentBanner.js";
|
|
6
6
|
import { useBooleanCookie } from "./useCookie.js";
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
// PostHog project token for event ingest and feature flags. `phc_` tokens are
|
|
8
|
+
// public by design (browser JS, no env var) and cannot read private project data.
|
|
9
|
+
// https://posthog.com/docs/privacy#is-it-ok-for-my-api-key-to-be-exposed-and-public
|
|
10
|
+
export const POSTHOG_PUBLIC_PROJECT_TOKEN = "phc_JDNTRxeh2li2sQTRO0IcOYMJcp8fPs5nTK9TU751nQK";
|
|
9
11
|
const POSTHOG_HOST = "https://ap.convex.dev";
|
|
12
|
+
const COOKIE_NAME = "allowsCookies";
|
|
13
|
+
// GPC wins over the stored cookie and does not write it, so turning GPC off
|
|
14
|
+
// restores the saved choice (or the banner if there is none). A missing or
|
|
15
|
+
// unrecognized cookie after the read defaults to `"undecided"`.
|
|
10
16
|
function resolveConsent(isReady, cookieValue, gpcOptOut) {
|
|
11
17
|
if (!isReady) {
|
|
12
18
|
return "unknown";
|
|
@@ -23,27 +29,23 @@ function resolveConsent(isReady, cookieValue, gpcOptOut) {
|
|
|
23
29
|
return "undecided";
|
|
24
30
|
}
|
|
25
31
|
export const ConsentContext = createContext(null);
|
|
26
|
-
export function WebAnalyticsProvider({ children
|
|
32
|
+
export function WebAnalyticsProvider({ children }) {
|
|
27
33
|
const { value, isReady, setValue } = useBooleanCookie(COOKIE_NAME);
|
|
28
34
|
const gpcOptOut = typeof navigator !== "undefined" && navigator.globalPrivacyControl === true;
|
|
29
35
|
const consent = resolveConsent(isReady, value, gpcOptOut);
|
|
36
|
+
// Initialize PostHog once, in cookieless mode. Consent is applied below.
|
|
30
37
|
useEffect(() => {
|
|
31
|
-
|
|
38
|
+
if (posthog.__loaded) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// Keep local/dev sessions out of production PostHog data.
|
|
32
42
|
if (process.env.NODE_ENV !== "production" ||
|
|
33
43
|
window.location.hostname === "localhost" ||
|
|
34
44
|
window.location.hostname === "127.0.0.1" ||
|
|
35
45
|
window.location.hostname === "[::1]") {
|
|
36
46
|
return;
|
|
37
47
|
}
|
|
38
|
-
|
|
39
|
-
if (posthog.__loaded) {
|
|
40
|
-
if (posthogConfig) {
|
|
41
|
-
posthog.set_config(posthogConfig);
|
|
42
|
-
}
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
// Initialize PostHog.
|
|
46
|
-
posthog.init(POSTHOG_KEY, {
|
|
48
|
+
posthog.init(POSTHOG_PUBLIC_PROJECT_TOKEN, {
|
|
47
49
|
api_host: POSTHOG_HOST,
|
|
48
50
|
ui_host: "https://us.posthog.com/",
|
|
49
51
|
// Automatically capture navigations using the History API.
|
|
@@ -53,28 +55,33 @@ export function WebAnalyticsProvider({ children, posthogConfig, }) {
|
|
|
53
55
|
// tracking. See more: https://posthog.com/tutorials/cookieless-tracking
|
|
54
56
|
opt_out_capturing_by_default: true,
|
|
55
57
|
cookieless_mode: "on_reject",
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
+
// Extends PostHog's built-in campaign params (gclid, rdt_cid, UTMs, etc.)
|
|
59
|
+
// onto events.
|
|
60
|
+
custom_campaign_params: ["dub_id"],
|
|
61
|
+
// Person profiles are enabled later, after consent is received.
|
|
62
|
+
person_profiles: "identified_only",
|
|
58
63
|
});
|
|
59
|
-
}, [
|
|
60
|
-
//
|
|
64
|
+
}, []);
|
|
65
|
+
// Keep PostHog capturing in sync with consent.
|
|
61
66
|
useEffect(() => {
|
|
62
|
-
|
|
63
|
-
if (consent === "unknown" || consent === "undecided" || !posthog.__loaded) {
|
|
67
|
+
if (!posthog.__loaded) {
|
|
64
68
|
return;
|
|
65
69
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (posthogConsent === targetConsent) {
|
|
70
|
+
// Skip until there's a real choice made.
|
|
71
|
+
if (consent === "unknown" || consent === "undecided") {
|
|
69
72
|
return;
|
|
70
73
|
}
|
|
74
|
+
// Skip opt-in/out when already in sync so returning visitors don't get a
|
|
75
|
+
// duplicate `$opt_in`.
|
|
71
76
|
if (consent === "consented") {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
if (posthog.get_explicit_consent_status() !== "granted") {
|
|
78
|
+
posthog.opt_in_capturing();
|
|
79
|
+
}
|
|
80
|
+
// Opting in replaces the cookieless identity with a persistent one, so we
|
|
81
|
+
// create the profile afterward. This is a no-op if it already exists.
|
|
82
|
+
posthog.createPersonProfile();
|
|
76
83
|
}
|
|
77
|
-
else {
|
|
84
|
+
else if (posthog.get_explicit_consent_status() !== "denied") {
|
|
78
85
|
posthog.opt_out_capturing();
|
|
79
86
|
}
|
|
80
87
|
}, [consent]);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { WebAnalyticsProvider, type WebAnalyticsProviderProps, type
|
|
1
|
+
export { WebAnalyticsProvider, type WebAnalyticsProviderProps, type ConsentStatus, } from "./WebAnalyticsProvider.js";
|
|
2
2
|
export { Consented, type ConsentedProps } from "./Consented.js";
|
|
3
3
|
export { useConsent } from "./useConsent.js";
|
|
4
4
|
export { capture, type CaptureOptions } from "./capture.js";
|