@chordcommerce/analytics 1.9.0 → 1.10.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,65 @@
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 { shopifyConsentAdapter } from './shopify.js';
11
+ export const addCdpConsent = (options) => {
12
+ let adapter;
13
+ /**
14
+ * Returns the adapter for the specified consent management platform.
15
+ */
16
+ const getAdapter = () => {
17
+ var _a;
18
+ switch ((_a = options.consent) === null || _a === void 0 ? void 0 : _a.toLowerCase()) {
19
+ case 'shopify':
20
+ return shopifyConsentAdapter();
21
+ default:
22
+ return null;
23
+ }
24
+ };
25
+ /**
26
+ * Returns whether the CDP should be loaded based on the current consent.
27
+ */
28
+ const shouldLoadCdp = () => __awaiter(void 0, void 0, void 0, function* () {
29
+ return new Promise((resolve) => {
30
+ const interval = setInterval(() => {
31
+ if (adapter === null || adapter === void 0 ? void 0 : adapter.userCanBeTracked()) {
32
+ clearInterval(interval);
33
+ resolve(true);
34
+ }
35
+ }, 500);
36
+ });
37
+ });
38
+ /**
39
+ * Updates consent for all future events.
40
+ */
41
+ const updateCdpConsent = (updatedConsent) => {
42
+ const cdp = window[`_${options.namespace}`];
43
+ if (cdp && typeof cdp.configure === 'function') {
44
+ cdp.configure({ privacy: updatedConsent });
45
+ }
46
+ else {
47
+ // CDP has not yet loaded via the snippet.
48
+ window.jitsuConfig = { privacy: updatedConsent };
49
+ }
50
+ };
51
+ /**
52
+ * Adds consent management by setting defaults and handling changes.
53
+ */
54
+ const init = () => __awaiter(void 0, void 0, void 0, function* () {
55
+ adapter = getAdapter();
56
+ if (adapter === null)
57
+ return Promise.resolve(false);
58
+ updateCdpConsent(adapter === null || adapter === void 0 ? void 0 : adapter.getCurrentConsent());
59
+ adapter === null || adapter === void 0 ? void 0 : adapter.handleConsentUpdates(updateCdpConsent);
60
+ return shouldLoadCdp();
61
+ });
62
+ return {
63
+ init,
64
+ };
65
+ };
@@ -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
+ };
@@ -23,6 +23,7 @@ export declare class ChordAnalytics<T extends ObjectTypes = ObjectTypes> {
23
23
  options: ChordAnalyticsOptions<T>;
24
24
  constructor(options: ChordAnalyticsOptions<T>);
25
25
  cdp: () => AnyAnalytics;
26
+ ccdp: () => AnyAnalytics;
26
27
  logger: (message?: any, ...optionalParams: any[]) => void;
27
28
  /**
28
29
  * Validate the body of a `track` event against the Chord tracking plan.
@@ -22,15 +22,24 @@ export class ChordAnalytics {
22
22
  */
23
23
  this.invoked = true;
24
24
  /*
25
- * Return the CDP instance or the queue.
25
+ * Return the CDP instance provided in `options.cdp`.
26
26
  */
27
27
  this.cdp = () => {
28
- const { cdp, namespace } = this.options;
29
- const a = cdp || (window === null || window === void 0 ? void 0 : window[`_${namespace}`]) || cdpQueue(namespace);
28
+ var _a;
29
+ const a = (_a = this.options) === null || _a === void 0 ? void 0 : _a.cdp;
30
30
  if (typeof a === 'function')
31
31
  return a();
32
32
  return a;
33
33
  };
34
+ /*
35
+ * Return the Chord CDP instance or queue.
36
+ */
37
+ this.ccdp = () => {
38
+ const { cdpDomain, cdpWriteKey, namespace } = this.options;
39
+ if (!cdpDomain || !cdpWriteKey)
40
+ return null;
41
+ return (window === null || window === void 0 ? void 0 : window[`_${namespace}`]) || cdpQueue(namespace);
42
+ };
34
43
  /*
35
44
  * Log a message to the console if `options.enableLogging` is true.
36
45
  */
@@ -90,6 +99,10 @@ export class ChordAnalytics {
90
99
  if (a && typeof a.track === 'function') {
91
100
  a.track(event, finalEventProps, options, () => resolve());
92
101
  }
102
+ const b = this.ccdp();
103
+ if (b && typeof b.track === 'function') {
104
+ b.track(event, finalEventProps, options, () => resolve());
105
+ }
93
106
  }
94
107
  catch (error) {
95
108
  this.logger(error);
@@ -105,6 +118,10 @@ export class ChordAnalytics {
105
118
  if (a && typeof a.identify === 'function') {
106
119
  a.identify(...props);
107
120
  }
121
+ const b = this.ccdp();
122
+ if (b && typeof b.identify === 'function') {
123
+ b.identify(...props);
124
+ }
108
125
  }
109
126
  catch (error) {
110
127
  this.logger(error);
@@ -119,6 +136,10 @@ export class ChordAnalytics {
119
136
  if (a && typeof a.page === 'function') {
120
137
  a.page({ meta: this.meta() });
121
138
  }
139
+ const b = this.ccdp();
140
+ if (b && typeof b.page === 'function') {
141
+ b.page({ meta: this.meta() });
142
+ }
122
143
  }
123
144
  catch (error) {
124
145
  this.logger(error);
@@ -130,6 +151,10 @@ export class ChordAnalytics {
130
151
  if (a && typeof a.reset === 'function') {
131
152
  a.reset();
132
153
  }
154
+ const b = this.ccdp();
155
+ if (b && typeof b.reset === 'function') {
156
+ b.reset();
157
+ }
133
158
  }
134
159
  catch (error) {
135
160
  this.logger(error);
@@ -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?: '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.0",
4
+ "version": "1.10.0",
5
5
  "author": "Chord Commerce <help@chord.co> (https://www.chord.co)",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",