@chordcommerce/analytics 1.22.2 → 1.23.0-beta.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.
@@ -0,0 +1,31 @@
1
+ import type { ConsentConfiguration } from '../../types/index.js';
2
+ declare global {
3
+ interface Window {
4
+ Cookiebot?: CookiebotApi;
5
+ }
6
+ }
7
+ /**
8
+ * Cookiebot's consent state. `method` describes how consent was given
9
+ * ('implied' | 'explicit' | null) and is intentionally excluded from categories.
10
+ */
11
+ interface CookiebotConsent {
12
+ necessary: boolean;
13
+ preferences: boolean;
14
+ statistics: boolean;
15
+ marketing: boolean;
16
+ method?: string | null;
17
+ }
18
+ interface CookiebotApi {
19
+ consent: CookiebotConsent;
20
+ consented: boolean;
21
+ declined: boolean;
22
+ hasResponse: boolean;
23
+ }
24
+ export declare const cookiebotConsentAdapter: () => {
25
+ getCurrentConsent: () => ConsentConfiguration;
26
+ handleConsentUpdates: (updateCdpConsent: (consent: ConsentConfiguration) => void) => void;
27
+ userCanBeTracked: () => boolean;
28
+ getCategories: () => Record<string, boolean>;
29
+ isConsentReady: () => boolean;
30
+ };
31
+ export {};
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cookiebotConsentAdapter = void 0;
4
+ /**
5
+ * The four standard Cookiebot consent categories.
6
+ */
7
+ var CONSENT_CATEGORIES = [
8
+ 'necessary',
9
+ 'preferences',
10
+ 'statistics',
11
+ 'marketing',
12
+ ];
13
+ /**
14
+ * Returns the Cookiebot global API if it is fully loaded.
15
+ *
16
+ * Duck-types on the `consent` object since window.Cookiebot may exist before
17
+ * the consent state has been populated. The explicit `api.consent` truthiness
18
+ * check is required because `typeof null === 'object'`, so a null consent would
19
+ * otherwise pass the guard and crash downstream reads.
20
+ */
21
+ var getApi = function () {
22
+ var api = window.Cookiebot;
23
+ if (api &&
24
+ typeof api === 'object' &&
25
+ api.consent &&
26
+ typeof api.consent === 'object') {
27
+ return api;
28
+ }
29
+ return undefined;
30
+ };
31
+ /**
32
+ * Returns the user's current Cookiebot consent categories.
33
+ * Reads FRESH from Cookiebot each time - no caching!
34
+ */
35
+ var getCategories = function () {
36
+ var api = getApi();
37
+ if (!api)
38
+ return {};
39
+ return CONSENT_CATEGORIES.reduce(function (acc, category) {
40
+ acc[category] = !!api.consent[category];
41
+ return acc;
42
+ }, {});
43
+ };
44
+ /**
45
+ * Returns whether the user can currently be tracked.
46
+ * Requires statistics consent (Cookiebot's analytics category) to be granted.
47
+ * Intentionally does not gate on `hasResponse`, so implied-consent setups —
48
+ * where statistics is allowed without an explicit banner interaction — are
49
+ * tracked immediately rather than blocked until the visitor clicks.
50
+ */
51
+ var userCanBeTracked = function () {
52
+ var api = getApi();
53
+ if (!api)
54
+ return false;
55
+ return !!api.consent.statistics;
56
+ };
57
+ /**
58
+ * Returns the user's current consent configuration.
59
+ */
60
+ var getCurrentConsent = function () {
61
+ var api = getApi();
62
+ if (!api)
63
+ return {};
64
+ return {
65
+ consentCategories: getCategories(),
66
+ };
67
+ };
68
+ /**
69
+ * Handles subsequent changes to consent configuration.
70
+ * CookiebotOnConsentReady fires on both accept and decline, so it covers the
71
+ * full set of consent transitions with a single listener.
72
+ */
73
+ var handleConsentUpdates = function (updateCdpConsent) {
74
+ window.addEventListener('CookiebotOnConsentReady', function () {
75
+ updateCdpConsent(getCurrentConsent());
76
+ });
77
+ };
78
+ var cookiebotConsentAdapter = function () {
79
+ return {
80
+ getCurrentConsent: getCurrentConsent,
81
+ handleConsentUpdates: handleConsentUpdates,
82
+ userCanBeTracked: userCanBeTracked,
83
+ getCategories: getCategories,
84
+ // Consent is ready once the visitor has responded to the banner (accept or
85
+ // decline) OR statistics is already granted (implied-consent setups, where
86
+ // consent is auto-granted without a banner click). Gating on hasResponse
87
+ // alone would queue implied-consent events indefinitely; gating on
88
+ // statistics alone would strand declined-statistics visitors. Requiring
89
+ // either preserves the opt-in "hold until the visitor decides" behavior.
90
+ isConsentReady: function () {
91
+ var api = getApi();
92
+ return !!api && (api.hasResponse || !!api.consent.statistics);
93
+ },
94
+ };
95
+ };
96
+ exports.cookiebotConsentAdapter = cookiebotConsentAdapter;
@@ -37,6 +37,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.addCdpConsent = void 0;
40
+ var cookiebot_js_1 = require("./cookiebot.js");
40
41
  var ketch_js_1 = require("./ketch.js");
41
42
  var onetrust_js_1 = require("./onetrust.js");
42
43
  var shopify_js_1 = require("./shopify.js");
@@ -53,6 +54,8 @@ var addCdpConsent = function (options) {
53
54
  var getAdapter = function () {
54
55
  var _a;
55
56
  switch ((_a = options.consent) === null || _a === void 0 ? void 0 : _a.toLowerCase()) {
57
+ case 'cookiebot':
58
+ return (0, cookiebot_js_1.cookiebotConsentAdapter)();
56
59
  case 'ketch':
57
60
  return (0, ketch_js_1.ketchConsentAdapter)();
58
61
  case 'onetrust':
@@ -30,7 +30,7 @@ export interface ChordAnalyticsOptions<T extends ObjectTypes = ObjectTypes> {
30
30
  cdp?: AnyAnalytics | (() => AnyAnalytics);
31
31
  cdpDomain?: string;
32
32
  cdpWriteKey?: string;
33
- consent?: 'ketch' | 'onetrust' | 'shopify' | 'segment';
33
+ consent?: 'cookiebot' | 'ketch' | 'onetrust' | 'shopify' | 'segment';
34
34
  awaitConsent?: boolean;
35
35
  debug?: boolean;
36
36
  enableLogging?: boolean;
@@ -0,0 +1,31 @@
1
+ import type { ConsentConfiguration } from '../../types/index.js';
2
+ declare global {
3
+ interface Window {
4
+ Cookiebot?: CookiebotApi;
5
+ }
6
+ }
7
+ /**
8
+ * Cookiebot's consent state. `method` describes how consent was given
9
+ * ('implied' | 'explicit' | null) and is intentionally excluded from categories.
10
+ */
11
+ interface CookiebotConsent {
12
+ necessary: boolean;
13
+ preferences: boolean;
14
+ statistics: boolean;
15
+ marketing: boolean;
16
+ method?: string | null;
17
+ }
18
+ interface CookiebotApi {
19
+ consent: CookiebotConsent;
20
+ consented: boolean;
21
+ declined: boolean;
22
+ hasResponse: boolean;
23
+ }
24
+ export declare const cookiebotConsentAdapter: () => {
25
+ getCurrentConsent: () => ConsentConfiguration;
26
+ handleConsentUpdates: (updateCdpConsent: (consent: ConsentConfiguration) => void) => void;
27
+ userCanBeTracked: () => boolean;
28
+ getCategories: () => Record<string, boolean>;
29
+ isConsentReady: () => boolean;
30
+ };
31
+ export {};
@@ -0,0 +1,92 @@
1
+ /**
2
+ * The four standard Cookiebot consent categories.
3
+ */
4
+ const CONSENT_CATEGORIES = [
5
+ 'necessary',
6
+ 'preferences',
7
+ 'statistics',
8
+ 'marketing',
9
+ ];
10
+ /**
11
+ * Returns the Cookiebot global API if it is fully loaded.
12
+ *
13
+ * Duck-types on the `consent` object since window.Cookiebot may exist before
14
+ * the consent state has been populated. The explicit `api.consent` truthiness
15
+ * check is required because `typeof null === 'object'`, so a null consent would
16
+ * otherwise pass the guard and crash downstream reads.
17
+ */
18
+ const getApi = () => {
19
+ const api = window.Cookiebot;
20
+ if (api &&
21
+ typeof api === 'object' &&
22
+ api.consent &&
23
+ typeof api.consent === 'object') {
24
+ return api;
25
+ }
26
+ return undefined;
27
+ };
28
+ /**
29
+ * Returns the user's current Cookiebot consent categories.
30
+ * Reads FRESH from Cookiebot each time - no caching!
31
+ */
32
+ const getCategories = () => {
33
+ const api = getApi();
34
+ if (!api)
35
+ return {};
36
+ return CONSENT_CATEGORIES.reduce((acc, category) => {
37
+ acc[category] = !!api.consent[category];
38
+ return acc;
39
+ }, {});
40
+ };
41
+ /**
42
+ * Returns whether the user can currently be tracked.
43
+ * Requires statistics consent (Cookiebot's analytics category) to be granted.
44
+ * Intentionally does not gate on `hasResponse`, so implied-consent setups —
45
+ * where statistics is allowed without an explicit banner interaction — are
46
+ * tracked immediately rather than blocked until the visitor clicks.
47
+ */
48
+ const userCanBeTracked = () => {
49
+ const api = getApi();
50
+ if (!api)
51
+ return false;
52
+ return !!api.consent.statistics;
53
+ };
54
+ /**
55
+ * Returns the user's current consent configuration.
56
+ */
57
+ const getCurrentConsent = () => {
58
+ const api = getApi();
59
+ if (!api)
60
+ return {};
61
+ return {
62
+ consentCategories: getCategories(),
63
+ };
64
+ };
65
+ /**
66
+ * Handles subsequent changes to consent configuration.
67
+ * CookiebotOnConsentReady fires on both accept and decline, so it covers the
68
+ * full set of consent transitions with a single listener.
69
+ */
70
+ const handleConsentUpdates = (updateCdpConsent) => {
71
+ window.addEventListener('CookiebotOnConsentReady', () => {
72
+ updateCdpConsent(getCurrentConsent());
73
+ });
74
+ };
75
+ export const cookiebotConsentAdapter = () => {
76
+ return {
77
+ getCurrentConsent,
78
+ handleConsentUpdates,
79
+ userCanBeTracked,
80
+ getCategories,
81
+ // Consent is ready once the visitor has responded to the banner (accept or
82
+ // decline) OR statistics is already granted (implied-consent setups, where
83
+ // consent is auto-granted without a banner click). Gating on hasResponse
84
+ // alone would queue implied-consent events indefinitely; gating on
85
+ // statistics alone would strand declined-statistics visitors. Requiring
86
+ // either preserves the opt-in "hold until the visitor decides" behavior.
87
+ isConsentReady: () => {
88
+ const api = getApi();
89
+ return !!api && (api.hasResponse || !!api.consent.statistics);
90
+ },
91
+ };
92
+ };
@@ -7,6 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
+ import { cookiebotConsentAdapter } from './cookiebot.js';
10
11
  import { ketchConsentAdapter } from './ketch.js';
11
12
  import { onetrustConsentAdapter } from './onetrust.js';
12
13
  import { shopifyConsentAdapter } from './shopify.js';
@@ -23,6 +24,8 @@ export const addCdpConsent = (options) => {
23
24
  const getAdapter = () => {
24
25
  var _a;
25
26
  switch ((_a = options.consent) === null || _a === void 0 ? void 0 : _a.toLowerCase()) {
27
+ case 'cookiebot':
28
+ return cookiebotConsentAdapter();
26
29
  case 'ketch':
27
30
  return ketchConsentAdapter();
28
31
  case 'onetrust':
@@ -30,7 +30,7 @@ export interface ChordAnalyticsOptions<T extends ObjectTypes = ObjectTypes> {
30
30
  cdp?: AnyAnalytics | (() => AnyAnalytics);
31
31
  cdpDomain?: string;
32
32
  cdpWriteKey?: string;
33
- consent?: 'ketch' | 'onetrust' | 'shopify' | 'segment';
33
+ consent?: 'cookiebot' | 'ketch' | 'onetrust' | 'shopify' | 'segment';
34
34
  awaitConsent?: boolean;
35
35
  debug?: boolean;
36
36
  enableLogging?: boolean;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@chordcommerce/analytics",
3
3
  "description": "Chord Commerce event tracking",
4
- "version": "1.22.2",
4
+ "version": "1.23.0-beta.1",
5
5
  "author": "Chord Commerce <help@chord.co> (https://www.chord.co)",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",