@chordcommerce/analytics 1.21.1 → 1.22.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.
- package/README.md +39 -10
- package/dist/cjs/client/identifiers.d.ts +51 -0
- package/dist/cjs/client/identifiers.js +193 -0
- package/dist/cjs/client/index.d.ts +31 -0
- package/dist/cjs/client/index.js +111 -1
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/types/client.d.ts +12 -0
- package/dist/client/identifiers.d.ts +51 -0
- package/dist/client/identifiers.js +131 -0
- package/dist/client/index.d.ts +31 -0
- package/dist/client/index.js +186 -83
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types/client.d.ts +12 -0
- package/package.json +1 -1
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shopify Identifier Sync
|
|
3
|
+
*
|
|
4
|
+
* Syncs CDP anonymous ID and Google Client ID to Shopify cart attributes.
|
|
5
|
+
* This enables server-side events (webhooks) to correlate with client-side tracking.
|
|
6
|
+
*/
|
|
7
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
8
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
9
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
10
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
11
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
12
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
13
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Get a cookie value by name
|
|
18
|
+
*/
|
|
19
|
+
export const getCookie = (name) => {
|
|
20
|
+
var _a;
|
|
21
|
+
if (typeof document === 'undefined')
|
|
22
|
+
return undefined;
|
|
23
|
+
return (_a = document.cookie
|
|
24
|
+
.split('; ')
|
|
25
|
+
.find((row) => row.startsWith(`${name}=`))) === null || _a === void 0 ? void 0 : _a.split('=')[1];
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Get default identifiers (CDP anonymous ID and Google Client ID)
|
|
29
|
+
*/
|
|
30
|
+
export const getDefaultIdentifiers = () => {
|
|
31
|
+
const attributes = {};
|
|
32
|
+
try {
|
|
33
|
+
// CDP anonymous ID (Jitsu/Chord CDP)
|
|
34
|
+
const anonymousId = getCookie('__eventn_id');
|
|
35
|
+
if (anonymousId) {
|
|
36
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
37
|
+
attributes._cdpAnonymousId = anonymousId;
|
|
38
|
+
}
|
|
39
|
+
// Google Client ID (strip the "GA1.1." or similar prefix)
|
|
40
|
+
const gaCookie = getCookie('_ga');
|
|
41
|
+
if (gaCookie) {
|
|
42
|
+
const googleClientId = gaCookie.substring(6);
|
|
43
|
+
if (googleClientId) {
|
|
44
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
45
|
+
attributes._googleClientId = googleClientId;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
// eslint-disable-next-line no-console
|
|
51
|
+
console.error('[Chord Analytics]: Error getting identifiers:', e);
|
|
52
|
+
}
|
|
53
|
+
return attributes;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Construct a URL for Shopify AJAX API calls
|
|
57
|
+
*/
|
|
58
|
+
const constructUrl = (path, baseUrl, localePath) => {
|
|
59
|
+
var _a, _b;
|
|
60
|
+
const defaultBaseUrl = typeof window !== 'undefined' ? (_a = window.location) === null || _a === void 0 ? void 0 : _a.origin : undefined;
|
|
61
|
+
// Access Shopify.routes.root for locale-aware URLs (cast needed as Window.Shopify type is partial)
|
|
62
|
+
const shopify = window === null || window === void 0 ? void 0 : window.Shopify;
|
|
63
|
+
const defaultLocalePath = ((_b = shopify === null || shopify === void 0 ? void 0 : shopify.routes) === null || _b === void 0 ? void 0 : _b.root) || '/';
|
|
64
|
+
const resolvedBaseUrl = baseUrl !== null && baseUrl !== void 0 ? baseUrl : defaultBaseUrl;
|
|
65
|
+
const resolvedLocalePath = localePath !== null && localePath !== void 0 ? localePath : defaultLocalePath;
|
|
66
|
+
const pathWithLocale = `${resolvedLocalePath}${path}`;
|
|
67
|
+
if (!resolvedBaseUrl) {
|
|
68
|
+
return pathWithLocale.startsWith('/')
|
|
69
|
+
? pathWithLocale
|
|
70
|
+
: `/${pathWithLocale}`;
|
|
71
|
+
}
|
|
72
|
+
return new URL(pathWithLocale, resolvedBaseUrl);
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Fetch the current Shopify cart
|
|
76
|
+
*/
|
|
77
|
+
export const getShopifyCart = (baseUrl, localePath) => __awaiter(void 0, void 0, void 0, function* () {
|
|
78
|
+
const url = constructUrl('cart.js', baseUrl, localePath);
|
|
79
|
+
const response = yield fetch(url);
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
throw new Error(`Failed to fetch cart: ${response.status}`);
|
|
82
|
+
}
|
|
83
|
+
return response.json();
|
|
84
|
+
});
|
|
85
|
+
/**
|
|
86
|
+
* Update Shopify cart attributes
|
|
87
|
+
*/
|
|
88
|
+
export const updateShopifyCartAttributes = (attributes, baseUrl, localePath) => __awaiter(void 0, void 0, void 0, function* () {
|
|
89
|
+
const url = constructUrl('cart/update.js', baseUrl, localePath);
|
|
90
|
+
const response = yield fetch(url, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers: {
|
|
93
|
+
'Content-Type': 'application/json',
|
|
94
|
+
},
|
|
95
|
+
body: JSON.stringify({ attributes }),
|
|
96
|
+
});
|
|
97
|
+
if (!response.ok) {
|
|
98
|
+
throw new Error(`Failed to update cart attributes: ${response.status}`);
|
|
99
|
+
}
|
|
100
|
+
return response.json();
|
|
101
|
+
});
|
|
102
|
+
/**
|
|
103
|
+
* Sync identifiers to Shopify cart attributes.
|
|
104
|
+
*
|
|
105
|
+
* This function:
|
|
106
|
+
* 1. Gets the current cart
|
|
107
|
+
* 2. Checks if identifiers already exist
|
|
108
|
+
* 3. If not, updates the cart with the identifiers
|
|
109
|
+
*
|
|
110
|
+
* @returns The updated cart, or undefined if no update was needed
|
|
111
|
+
*/
|
|
112
|
+
export const syncIdentifiersToCart = (options = {}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
113
|
+
try {
|
|
114
|
+
const { baseUrl, localePath, identifiers = getDefaultIdentifiers } = options;
|
|
115
|
+
const cart = yield getShopifyCart(baseUrl, localePath);
|
|
116
|
+
const attributesToAdd = identifiers();
|
|
117
|
+
// Check if all identifiers already exist with correct values
|
|
118
|
+
const identifiersAlreadyExist = Object.keys(attributesToAdd).every((key) => { var _a; return ((_a = cart === null || cart === void 0 ? void 0 : cart.attributes) === null || _a === void 0 ? void 0 : _a[key]) === attributesToAdd[key]; });
|
|
119
|
+
if (identifiersAlreadyExist) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
// Update cart with new identifiers
|
|
123
|
+
const updatedCart = yield updateShopifyCartAttributes(attributesToAdd, baseUrl, localePath);
|
|
124
|
+
return updatedCart;
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
// eslint-disable-next-line no-console
|
|
128
|
+
console.error('[Chord Analytics]: Error syncing identifiers to cart:', e);
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
});
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AnyAnalytics, EventProperties, Options, ValidateResult, ChordAnalyticsOptions, AnyOptions, CartViewedInput, IdentifyTraits, CheckoutStartedInput, CheckoutStepCompletedInput, CheckoutStepViewedInput, EmailCapturedInput, ProductListViewedInput, ProductListFilteredInput, ProductAddedInput, ProductClickedInput, VariantClickedInput, ProductRemovedInput, ProductViewedInput, ProductsSearchedInput, CouponAppliedInput, CouponDeniedInput, CouponEnteredInput, CouponRemovedInput, SignedInInput, SignedOutInput, SignedUpInput, LoginStartedInput, ObjectTypes, OrderCompletedInput, SubscriptionCancelledInput, NavigationClickedInput, PaymentInfoEnteredInput } from '../types/index.js';
|
|
2
2
|
import { AnalyticsMiddleware } from '../types/client.js';
|
|
3
|
+
import { type IdentifierSyncOptions, type ShopifyCart } from './identifiers.js';
|
|
3
4
|
export interface ChordAnalyticsSnippet {
|
|
4
5
|
_loadOptions: ChordAnalyticsOptions<ObjectTypes>;
|
|
5
6
|
invoked: boolean;
|
|
@@ -151,6 +152,36 @@ export declare class ChordAnalytics<T extends ObjectTypes = ObjectTypes> {
|
|
|
151
152
|
*/
|
|
152
153
|
private sendPageEvent;
|
|
153
154
|
reset: () => Promise<void>;
|
|
155
|
+
/**
|
|
156
|
+
* Handler for postMessage events from the Shopify Web Pixel sandbox.
|
|
157
|
+
* Bound to the instance for proper `this` context and cleanup.
|
|
158
|
+
*/
|
|
159
|
+
private webPixelMessageHandler;
|
|
160
|
+
/**
|
|
161
|
+
* Set up a listener for postMessage events from the Shopify Web Pixel sandbox.
|
|
162
|
+
* This allows the lightweight web pixel (running in Shopify's sandboxed iframe)
|
|
163
|
+
* to forward analytics events to this ChordAnalytics instance on the storefront.
|
|
164
|
+
*
|
|
165
|
+
* The lightweight web pixel sends messages in the format:
|
|
166
|
+
* - { source: 'chord-web-pixel', type: 'track', payload: [eventName, properties] }
|
|
167
|
+
* - { source: 'chord-web-pixel', type: 'identify', payload: [userId, traits] }
|
|
168
|
+
* - { source: 'chord-web-pixel', type: 'page', payload: [properties] }
|
|
169
|
+
*
|
|
170
|
+
* @returns A cleanup function to remove the listener
|
|
171
|
+
*/
|
|
172
|
+
setupWebPixelListener: () => (() => void);
|
|
173
|
+
/**
|
|
174
|
+
* Sync CDP anonymous ID and Google Client ID to Shopify cart attributes.
|
|
175
|
+
* This enables server-side events (webhooks) to correlate with client-side tracking.
|
|
176
|
+
*
|
|
177
|
+
* By default, syncs:
|
|
178
|
+
* - `_cdpAnonymousId`: from the `__eventn_id` cookie (Chord/Jitsu CDP)
|
|
179
|
+
* - `_googleClientId`: from the `_ga` cookie (Google Analytics)
|
|
180
|
+
*
|
|
181
|
+
* @param options - Optional configuration for the sync
|
|
182
|
+
* @returns The updated cart, or undefined if no update was needed or an error occurred
|
|
183
|
+
*/
|
|
184
|
+
syncIdentifiersToCart: (options?: IdentifierSyncOptions) => Promise<ShopifyCart | undefined>;
|
|
154
185
|
trackCartViewed: (props: CartViewedInput<T>, options?: AnyOptions) => Promise<void>;
|
|
155
186
|
trackCheckoutStarted: (props: CheckoutStartedInput<T>, options?: AnyOptions) => Promise<void>;
|
|
156
187
|
trackCheckoutStepCompleted: (props: CheckoutStepCompletedInput, options?: AnyOptions) => Promise<void>;
|