@wapitee/typhoonx-hydrogen 0.5.0 → 0.7.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.
package/README.md CHANGED
@@ -48,6 +48,10 @@ const {nonce, header, NonceProvider} = createContentSecurityPolicy({
48
48
  | `shopId` | `string` | yes | Shopify store ID |
49
49
  | `cookieDomain` | `string` | no | Cookie `Domain` for a first-party client id. Defaults to the current hostname's apex domain (e.g. `.example.com`) |
50
50
 
51
+ ### Meta Pixel
52
+
53
+ If the storefront runs Meta Pixel, every event also carries its `_fbp` cookie as `fbp` and its `_fbc` cookie as `fbc`. When `_fbc` is not set yet but the page URL has `fbclid`, `fbc` is built as `fb.1.<timestamp>.<fbclid>`. Missing values are omitted.
54
+
51
55
  ## License
52
56
 
53
57
  [MIT](./LICENSE) © Wapitee Interactive
@@ -1 +1 @@
1
- {"version":3,"file":"TyphoonX.d.ts","sourceRoot":"","sources":["../src/TyphoonX.tsx"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA2BD,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,KAAK,EAAE,aAAa,sCAYpD"}
1
+ {"version":3,"file":"TyphoonX.d.ts","sourceRoot":"","sources":["../src/TyphoonX.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAqCD,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,KAAK,EAAE,aAAa,sCAuBpD"}
package/dist/TyphoonX.js CHANGED
@@ -1,14 +1,22 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { flattenConnection, parseGid, useAnalytics } from '@shopify/hydrogen';
3
3
  import { useEffect, useRef, useState } from 'react';
4
- import { getOrCreateClientId } from './client-id.js';
4
+ import { getOrCreateClientId, hasClientId } from './client-id.js';
5
+ import { getMetaPixelIds } from './meta-pixel.js';
5
6
  const COLLECT_ENDPOINT = 'https://spell.typhoonx.io/api/v1/receive';
6
7
  export default function TyphoonX(props) {
8
+ const { subscribe } = useAnalytics();
9
+ const isInsideProvider = subscribe.length > 0;
7
10
  const [isClient, setIsClient] = useState(false);
8
11
  useEffect(() => {
9
12
  setIsClient(true);
10
13
  }, []);
11
- if (!isClient) {
14
+ useEffect(() => {
15
+ if (!isInsideProvider) {
16
+ console.error('[TyphoonX] <TyphoonX> must be rendered inside <Analytics.Provider>.');
17
+ }
18
+ }, [isInsideProvider]);
19
+ if (!isClient || !isInsideProvider) {
12
20
  return null;
13
21
  }
14
22
  return _jsx(TyphoonXClient, { ...props });
@@ -18,6 +26,17 @@ function TyphoonXClient({ cookieDomain, merchantId, shopId }) {
18
26
  const { ready } = register('TyphoonX');
19
27
  const currentUrlRef = useRef(null);
20
28
  const previousUrlRef = useRef(null);
29
+ useEffect(() => {
30
+ try {
31
+ if (!hasClientId()) {
32
+ window.localStorage.setItem('__typhoon_first_visit_time', new Date().toISOString());
33
+ window.localStorage.setItem('__typhoon_first_visit_url', window.location.href);
34
+ }
35
+ }
36
+ catch {
37
+ // Do nothing
38
+ }
39
+ }, [hasClientId()]);
21
40
  useEffect(() => {
22
41
  const clientId = getOrCreateClientId(cookieDomain);
23
42
  const referrerFor = (url) => {
@@ -41,6 +60,7 @@ function TyphoonXClient({ cookieDomain, merchantId, shopId }) {
41
60
  timestamp: new Date().toISOString(),
42
61
  user_agent: window.navigator.userAgent,
43
62
  ...(currency === undefined ? {} : { currency }),
63
+ ...getMetaPixelIds(url),
44
64
  });
45
65
  subscribe('cart_viewed', (data) => {
46
66
  const { cart } = data;
@@ -72,10 +92,28 @@ function TyphoonXClient({ cookieDomain, merchantId, shopId }) {
72
92
  });
73
93
  });
74
94
  subscribe('page_viewed', (data) => {
95
+ const payload = shared(data.url);
75
96
  send({
76
- ...shared(data.url),
97
+ ...payload,
77
98
  event: 'page_view',
78
99
  });
100
+ try {
101
+ const visitTime = window.localStorage.getItem('__typhoon_first_visit_time');
102
+ const visitUrl = window.localStorage.getItem('__typhoon_first_visit_url');
103
+ if (!visitTime || !visitUrl)
104
+ return;
105
+ send({
106
+ ...payload,
107
+ request_page_url: visitUrl,
108
+ timestamp: visitTime,
109
+ event: 'first_visit',
110
+ });
111
+ window.localStorage.removeItem('__typhoon_first_visit_time');
112
+ window.localStorage.removeItem('__typhoon_first_visit_url');
113
+ }
114
+ catch {
115
+ // Do nothing
116
+ }
79
117
  });
80
118
  subscribe('product_added_to_cart', (data) => {
81
119
  const { currentLine } = data;
@@ -113,8 +151,8 @@ function TyphoonXClient({ cookieDomain, merchantId, shopId }) {
113
151
  quantity,
114
152
  },
115
153
  ],
116
- value: String(Number(prevLine.cost.totalAmount.amount)
117
- - Number(currentLine?.cost.totalAmount.amount ?? 0)),
154
+ value: String(Number(prevLine.cost.totalAmount.amount) -
155
+ Number(currentLine?.cost.totalAmount.amount ?? 0)),
118
156
  });
119
157
  });
120
158
  subscribe('product_viewed', (data) => {
@@ -1,3 +1,5 @@
1
+ /** Whether `__typhoon_client_id` is already set. */
2
+ export declare function hasClientId(): boolean;
1
3
  /** Existing `__typhoon_client_id`, or a new UUID cookie on the apex domain. */
2
4
  export declare function getOrCreateClientId(cookieDomain?: string): string;
3
5
  //# sourceMappingURL=client-id.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client-id.d.ts","sourceRoot":"","sources":["../src/client-id.ts"],"names":[],"mappings":"AAwBA,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAWjE"}
1
+ {"version":3,"file":"client-id.d.ts","sourceRoot":"","sources":["../src/client-id.ts"],"names":[],"mappings":"AAwBA,oDAAoD;AACpD,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAWjE"}
package/dist/client-id.js CHANGED
@@ -8,6 +8,10 @@ const cookies = new Cookies(null, {
8
8
  sameSite: 'lax',
9
9
  maxAge: MAX_AGE,
10
10
  });
11
+ /** Whether `__typhoon_client_id` is already set. */
12
+ export function hasClientId() {
13
+ return cookies.get(COOKIE, { doNotParse: true }) !== undefined;
14
+ }
11
15
  /** Existing `__typhoon_client_id`, or a new UUID cookie on the apex domain. */
12
16
  export function getOrCreateClientId(cookieDomain) {
13
17
  const existing = cookies.get(COOKIE, { doNotParse: true });
@@ -0,0 +1,7 @@
1
+ export interface MetaPixelIds {
2
+ fbc?: string;
3
+ fbp?: string;
4
+ }
5
+ /** Meta Pixel `_fbp` / `_fbc` cookies, with `_fbc` falling back to `url`'s `fbclid`. */
6
+ export declare function getMetaPixelIds(url: string): MetaPixelIds;
7
+ //# sourceMappingURL=meta-pixel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta-pixel.d.ts","sourceRoot":"","sources":["../src/meta-pixel.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAQzD"}
@@ -0,0 +1,23 @@
1
+ import CookieLib from 'universal-cookie';
2
+ // Runtime default export is the class.
3
+ const cookies = new CookieLib();
4
+ /** Meta Pixel `_fbp` / `_fbc` cookies, with `_fbc` falling back to `url`'s `fbclid`. */
5
+ export function getMetaPixelIds(url) {
6
+ const fbp = cookies.get('_fbp', { doNotParse: true });
7
+ const fbc = cookies.get('_fbc', { doNotParse: true }) ?? fbcFromUrl(url);
8
+ return {
9
+ ...(fbc === undefined ? {} : { fbc }),
10
+ ...(fbp === undefined ? {} : { fbp }),
11
+ };
12
+ }
13
+ function fbcFromUrl(url) {
14
+ let fbclid;
15
+ try {
16
+ fbclid = new URL(url).searchParams.get('fbclid');
17
+ }
18
+ catch {
19
+ return undefined;
20
+ }
21
+ // Meta specifies subdomain index 1 for an `_fbc` built outside the Pixel.
22
+ return fbclid ? `fb.1.${String(Date.now())}.${fbclid}` : undefined;
23
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wapitee/typhoonx-hydrogen",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "TyphoonX Hydrogen Analytics subscriber",
5
5
  "keywords": [
6
6
  "typhoonx",