@chordcommerce/analytics 1.9.1 → 1.11.0

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.
@@ -1,3 +1,3 @@
1
1
  import type { ChordAnalyticsOptions, ChordAnalyticsQueue } from '../types/index.js';
2
- export declare const addCdpSnippet: (options: ChordAnalyticsOptions) => void;
2
+ export declare const addCdpSnippet: (options: ChordAnalyticsOptions) => Promise<void>;
3
3
  export declare const cdpQueue: (namespace: string) => ChordAnalyticsQueue;
@@ -1,7 +1,17 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { addCdpConsent } from './consent/index.js';
1
11
  /*
2
12
  * Adds the Chord CDP snippet to the page.
3
13
  */
4
- export const addCdpSnippet = (options) => {
14
+ export const addCdpSnippet = (options) => __awaiter(void 0, void 0, void 0, function* () {
5
15
  if (!document || typeof document.createElement === 'undefined')
6
16
  return;
7
17
  const s = document.createElement('script');
@@ -11,8 +21,10 @@ export const addCdpSnippet = (options) => {
11
21
  s.setAttribute('data-init-only', 'true');
12
22
  s.setAttribute('data-namespace', `_${options.namespace}`);
13
23
  s.setAttribute('data-write-key', options.cdpWriteKey);
24
+ if (options.consent)
25
+ yield addCdpConsent(options).init();
14
26
  document.head.appendChild(s);
15
- };
27
+ });
16
28
  /*
17
29
  * Returns the CDP queue to hold events ahead of CDP initialization.
18
30
  */
@@ -0,0 +1,11 @@
1
+ import type { ChordAnalyticsOptions, ConsentConfiguration } from '../../types/index.js';
2
+ declare global {
3
+ interface Window {
4
+ jitsuConfig: {
5
+ privacy: ConsentConfiguration;
6
+ };
7
+ }
8
+ }
9
+ export declare const addCdpConsent: (options: ChordAnalyticsOptions) => {
10
+ init: () => Promise<boolean>;
11
+ };
@@ -0,0 +1,69 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { ketchConsentAdapter } from './ketch.js';
11
+ import { shopifyConsentAdapter } from './shopify.js';
12
+ export const addCdpConsent = (options) => {
13
+ let adapter;
14
+ /**
15
+ * Returns the adapter for the specified consent management platform.
16
+ */
17
+ const getAdapter = () => {
18
+ var _a;
19
+ switch ((_a = options.consent) === null || _a === void 0 ? void 0 : _a.toLowerCase()) {
20
+ case 'ketch':
21
+ return ketchConsentAdapter();
22
+ case 'shopify':
23
+ return shopifyConsentAdapter();
24
+ default:
25
+ return null;
26
+ }
27
+ };
28
+ /**
29
+ * Returns whether the CDP should be loaded based on the current consent.
30
+ */
31
+ const shouldLoadCdp = () => __awaiter(void 0, void 0, void 0, function* () {
32
+ return new Promise((resolve) => {
33
+ const interval = setInterval(() => {
34
+ if (adapter === null || adapter === void 0 ? void 0 : adapter.userCanBeTracked()) {
35
+ clearInterval(interval);
36
+ resolve(true);
37
+ }
38
+ }, 500);
39
+ });
40
+ });
41
+ /**
42
+ * Updates consent for all future events.
43
+ */
44
+ const updateCdpConsent = (updatedConsent) => {
45
+ const cdp = window[`_${options.namespace}`];
46
+ if (cdp && typeof cdp.configure === 'function') {
47
+ cdp.configure({ privacy: updatedConsent });
48
+ }
49
+ else {
50
+ // CDP has not yet loaded via the snippet.
51
+ window.jitsuConfig = { privacy: updatedConsent };
52
+ }
53
+ };
54
+ /**
55
+ * Adds consent management by setting defaults and handling changes.
56
+ */
57
+ const init = () => __awaiter(void 0, void 0, void 0, function* () {
58
+ adapter = getAdapter();
59
+ if (adapter === null)
60
+ return Promise.resolve(false);
61
+ yield shouldLoadCdp();
62
+ updateCdpConsent(adapter === null || adapter === void 0 ? void 0 : adapter.getCurrentConsent());
63
+ adapter === null || adapter === void 0 ? void 0 : adapter.handleConsentUpdates(updateCdpConsent);
64
+ return Promise.resolve(true);
65
+ });
66
+ return {
67
+ init,
68
+ };
69
+ };
@@ -0,0 +1,16 @@
1
+ import type { ConsentConfiguration } from '../../types/index.js';
2
+ declare global {
3
+ interface Window {
4
+ ketch?: (action: string, eventName: string, handler: () => void) => undefined;
5
+ ketchConsent?: KetchConsentCategories;
6
+ }
7
+ }
8
+ interface KetchConsentCategories {
9
+ [key: string]: boolean;
10
+ }
11
+ export declare const ketchConsentAdapter: () => {
12
+ getCurrentConsent: () => ConsentConfiguration;
13
+ handleConsentUpdates: (updateCdpConsent: (consent: ConsentConfiguration) => void) => void;
14
+ userCanBeTracked: () => boolean;
15
+ };
16
+ export {};
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Returns Ketch's consent categories.
3
+ */
4
+ const getApi = () => window.ketch && window.ketchConsent;
5
+ /**
6
+ * Returns whether the user can currently be tracked.
7
+ */
8
+ const userCanBeTracked = () => {
9
+ const api = getApi();
10
+ return api && !!Object.keys(api).length;
11
+ };
12
+ /**
13
+ * Returns the user's current consent configuration.
14
+ */
15
+ const getCurrentConsent = () => {
16
+ const api = getApi();
17
+ if (!api)
18
+ return {};
19
+ return {
20
+ consentCategories: Object.assign({}, api),
21
+ dontSend: !userCanBeTracked(),
22
+ };
23
+ };
24
+ /**
25
+ * Handles subsequent changes to consent configuration.
26
+ */
27
+ const handleConsentUpdates = (updateCdpConsent) => {
28
+ window.ketch('addListener', 'consent', () => {
29
+ updateCdpConsent(getCurrentConsent());
30
+ });
31
+ };
32
+ export const ketchConsentAdapter = () => {
33
+ return {
34
+ getCurrentConsent,
35
+ handleConsentUpdates,
36
+ userCanBeTracked,
37
+ };
38
+ };
@@ -0,0 +1,21 @@
1
+ import type { ConsentConfiguration } from '../../types/index.js';
2
+ declare global {
3
+ interface Window {
4
+ Shopify?: {
5
+ customerPrivacy?: ShopifyCustomerPrivacyApi;
6
+ };
7
+ }
8
+ }
9
+ interface ShopifyCustomerPrivacyApi {
10
+ getRegion: () => string;
11
+ analyticsProcessingAllowed: () => boolean;
12
+ marketingAllowed: () => boolean;
13
+ preferencesProcessingAllowed: () => boolean;
14
+ saleOfDataAllowed: () => boolean;
15
+ }
16
+ export declare const shopifyConsentAdapter: () => {
17
+ getCurrentConsent: () => ConsentConfiguration;
18
+ handleConsentUpdates: (updateCdpConsent: (consent: ConsentConfiguration) => void) => void;
19
+ userCanBeTracked: () => boolean;
20
+ };
21
+ export {};
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Returns Shopify's Customer Privacy API.
3
+ */
4
+ const getApi = () => { var _a; return (_a = window.Shopify) === null || _a === void 0 ? void 0 : _a.customerPrivacy; };
5
+ /**
6
+ * Returns whether the user can currently be tracked.
7
+ */
8
+ const userCanBeTracked = () => {
9
+ const api = getApi();
10
+ if (!api)
11
+ return false;
12
+ return !!(api.getRegion() &&
13
+ api.analyticsProcessingAllowed() &&
14
+ api.marketingAllowed());
15
+ };
16
+ /**
17
+ * Returns the user's current consent configuration.
18
+ */
19
+ const getCurrentConsent = () => {
20
+ const api = getApi();
21
+ if (!api)
22
+ return {};
23
+ return {
24
+ consentCategories: {
25
+ analytics: api.analyticsProcessingAllowed(),
26
+ marketing: api.marketingAllowed(),
27
+ preferences: api.preferencesProcessingAllowed(),
28
+ sale_of_data: api.saleOfDataAllowed(),
29
+ },
30
+ dontSend: !userCanBeTracked(),
31
+ };
32
+ };
33
+ /**
34
+ * Handles subsequent changes to consent configuration.
35
+ */
36
+ const handleConsentUpdates = (updateCdpConsent) => {
37
+ document.addEventListener('visitorConsentCollected', () => {
38
+ updateCdpConsent(getCurrentConsent());
39
+ });
40
+ };
41
+ export const shopifyConsentAdapter = () => {
42
+ return {
43
+ getCurrentConsent,
44
+ handleConsentUpdates,
45
+ userCanBeTracked,
46
+ };
47
+ };
@@ -30,6 +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' | 'shopify';
33
34
  debug?: boolean;
34
35
  enableLogging?: boolean;
35
36
  formatters: ChordAnalyticsFormatters<T>;
@@ -0,0 +1,11 @@
1
+ export interface ConsentAdapter {
2
+ getCurrentConsent: () => ConsentConfiguration;
3
+ handleConsentUpdates: (fn: (ConsentConfiguration: any) => void) => void;
4
+ userCanBeTracked: () => boolean;
5
+ }
6
+ export interface ConsentConfiguration {
7
+ consentCategories?: {
8
+ [key: string]: boolean;
9
+ };
10
+ dontSend?: boolean;
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -2,5 +2,6 @@ export type { AnalyticsSnippet, Analytics, AnalyticsBrowser, EventProperties, Op
2
2
  export * from './objects.js';
3
3
  export * from './typewriter.js';
4
4
  export * from './client.js';
5
+ export * from './consent.js';
5
6
  export * from './input.js';
6
7
  export * from './formatters.js';
@@ -1,5 +1,6 @@
1
1
  export * from './objects.js';
2
2
  export * from './typewriter.js';
3
3
  export * from './client.js';
4
+ export * from './consent.js';
4
5
  export * from './input.js';
5
6
  export * from './formatters.js';
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.9.1",
4
+ "version": "1.11.0",
5
5
  "author": "Chord Commerce <help@chord.co> (https://www.chord.co)",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",