@engagex/react 0.1.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 +87 -0
- package/dist/index.cjs +131 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +81 -0
- package/dist/index.d.ts +81 -0
- package/dist/index.js +103 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# @engagex/react
|
|
2
|
+
|
|
3
|
+
React / Next.js wrapper for **EngageX** tracking. It loads `engage.v1.js` from
|
|
4
|
+
your tracking host and gives you a typed `<EngageProvider>`, `sendEngageEvent()`
|
|
5
|
+
and `engageIdentify()` — with **automatic page-view tracking across SPA route
|
|
6
|
+
changes**.
|
|
7
|
+
|
|
8
|
+
> Like `@next/third-parties`, this package does **not** bundle the tracker — it
|
|
9
|
+
> injects the script from your host, so it auto-updates, keeps endpoint
|
|
10
|
+
> auto-detection, and shares the anonymous `_cdp_tid` identity with your landing
|
|
11
|
+
> pages / embedded forms.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm add @engagex/react
|
|
17
|
+
# or: pnpm add @engagex/react
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Next.js (App Router)
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
// app/layout.tsx
|
|
24
|
+
import { EngageProvider } from '@engagex/react'
|
|
25
|
+
|
|
26
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
27
|
+
return (
|
|
28
|
+
<html>
|
|
29
|
+
<body>
|
|
30
|
+
<EngageProvider
|
|
31
|
+
publicKey="pk_live_xxx"
|
|
32
|
+
host="https://engage.data360.asia"
|
|
33
|
+
/>
|
|
34
|
+
{children}
|
|
35
|
+
</body>
|
|
36
|
+
</html>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`page.viewed` fires automatically on every route change — no `useEffect` needed.
|
|
42
|
+
|
|
43
|
+
## Send events / identify (any client component)
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
'use client'
|
|
47
|
+
import { sendEngageEvent, engageIdentify } from '@engagex/react'
|
|
48
|
+
|
|
49
|
+
// business / behavioural event
|
|
50
|
+
sendEngageEvent('order.placed', { order_id: 'DH1', total: 1990000, currency: 'VND' })
|
|
51
|
+
|
|
52
|
+
// identify — resolving key (phone/email/zalo_id, ≥1) + optional traits + consent
|
|
53
|
+
engageIdentify(
|
|
54
|
+
{ phone: '0912345678', name: 'Nguyễn Văn An', gender: 'male', birthday: '1990-12-31' },
|
|
55
|
+
{ consent: true, purpose: 'marketing' },
|
|
56
|
+
)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- Traits are applied **fill-missing** (never overwrite existing profile data).
|
|
60
|
+
- Re-calling `identify` with the same phone **updates**, it does not create a
|
|
61
|
+
duplicate. Identifying a different person rotates the anonymous id.
|
|
62
|
+
|
|
63
|
+
## Plain React (Vite / CRA)
|
|
64
|
+
|
|
65
|
+
Same API. `<EngageProvider>` tracks route changes via the History API, so it
|
|
66
|
+
works with React Router too — no extra wiring.
|
|
67
|
+
|
|
68
|
+
## API
|
|
69
|
+
|
|
70
|
+
| Export | Description |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| `<EngageProvider publicKey host autoPageview? branchId? />` | Loads the SDK + auto page-view. Mount once at the root. |
|
|
73
|
+
| `sendEngageEvent(event, properties?)` | Forward a `track` call (SSR-safe). |
|
|
74
|
+
| `engageIdentify(identity, consent?)` | Forward an `identify` call (SSR-safe). |
|
|
75
|
+
|
|
76
|
+
### `<EngageProvider>` props
|
|
77
|
+
|
|
78
|
+
| Prop | Type | Default | |
|
|
79
|
+
| --- | --- | --- | --- |
|
|
80
|
+
| `publicKey` | `string` | — | Source public key (`pk_...`). |
|
|
81
|
+
| `host` | `string` | — | Tracking host serving `engage.v1.js`. |
|
|
82
|
+
| `autoPageview` | `boolean` | `true` | Auto-fire `page.viewed` on route change. |
|
|
83
|
+
| `branchId` | `number \| string` | — | Attached to every event. |
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/index.ts
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
|
+
EngageProvider: () => EngageProvider,
|
|
25
|
+
engageIdentify: () => engageIdentify,
|
|
26
|
+
sendEngageEvent: () => sendEngageEvent
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
|
|
30
|
+
// src/provider.tsx
|
|
31
|
+
var import_react = require("react");
|
|
32
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
33
|
+
var STUB_METHODS = ["init", "track", "identify", "flush"];
|
|
34
|
+
var NAV_EVENT = "engagex:navigation";
|
|
35
|
+
function EngageProvider({
|
|
36
|
+
publicKey,
|
|
37
|
+
host,
|
|
38
|
+
autoPageview = true,
|
|
39
|
+
branchId,
|
|
40
|
+
children
|
|
41
|
+
}) {
|
|
42
|
+
(0, import_react.useEffect)(() => {
|
|
43
|
+
if (typeof window === "undefined" || !publicKey || !host) return;
|
|
44
|
+
const w = window;
|
|
45
|
+
const base = host.replace(/\/+$/, "");
|
|
46
|
+
if (!w.__engagex_loaded) {
|
|
47
|
+
w.__engagex_loaded = true;
|
|
48
|
+
const name = "engage";
|
|
49
|
+
if (!w[name]) {
|
|
50
|
+
w[name] = { _q: [] };
|
|
51
|
+
STUB_METHODS.forEach((m) => {
|
|
52
|
+
w[name][m] = function() {
|
|
53
|
+
w[name]._q.push([m, arguments]);
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
const el = document.createElement("script");
|
|
58
|
+
el.async = true;
|
|
59
|
+
el.src = base + "/engage.v1.js";
|
|
60
|
+
const first = document.getElementsByTagName("script")[0];
|
|
61
|
+
if (first && first.parentNode) first.parentNode.insertBefore(el, first);
|
|
62
|
+
else document.head.appendChild(el);
|
|
63
|
+
w[name].init({ key: publicKey, endpoint: base, autoPageview: false, branch_id: branchId });
|
|
64
|
+
}
|
|
65
|
+
if (!autoPageview) return;
|
|
66
|
+
const fire = () => {
|
|
67
|
+
try {
|
|
68
|
+
w.engage?.track("page.viewed", { url: window.location.href });
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
fire();
|
|
73
|
+
const origPush = window.history.pushState;
|
|
74
|
+
const origReplace = window.history.replaceState;
|
|
75
|
+
let patchedHere = false;
|
|
76
|
+
if (!w.__engagex_history_patched) {
|
|
77
|
+
w.__engagex_history_patched = true;
|
|
78
|
+
patchedHere = true;
|
|
79
|
+
window.history.pushState = function(...args) {
|
|
80
|
+
const r = origPush.apply(this, args);
|
|
81
|
+
window.dispatchEvent(new Event(NAV_EVENT));
|
|
82
|
+
return r;
|
|
83
|
+
};
|
|
84
|
+
window.history.replaceState = function(...args) {
|
|
85
|
+
const r = origReplace.apply(this, args);
|
|
86
|
+
window.dispatchEvent(new Event(NAV_EVENT));
|
|
87
|
+
return r;
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
window.addEventListener(NAV_EVENT, fire);
|
|
91
|
+
window.addEventListener("popstate", fire);
|
|
92
|
+
return () => {
|
|
93
|
+
window.removeEventListener(NAV_EVENT, fire);
|
|
94
|
+
window.removeEventListener("popstate", fire);
|
|
95
|
+
if (patchedHere) {
|
|
96
|
+
window.history.pushState = origPush;
|
|
97
|
+
window.history.replaceState = origReplace;
|
|
98
|
+
w.__engagex_history_patched = false;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}, [publicKey, host, autoPageview, branchId]);
|
|
102
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/engage.ts
|
|
106
|
+
function resolve() {
|
|
107
|
+
if (typeof window === "undefined") return void 0;
|
|
108
|
+
return window.engage;
|
|
109
|
+
}
|
|
110
|
+
function warnNotReady() {
|
|
111
|
+
if (typeof console !== "undefined") {
|
|
112
|
+
console.warn("[engagex] window.engage is not ready \u2014 did you mount <EngageProvider>?");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function sendEngageEvent(event, properties) {
|
|
116
|
+
const e = resolve();
|
|
117
|
+
if (e && typeof e.track === "function") e.track(event, properties);
|
|
118
|
+
else warnNotReady();
|
|
119
|
+
}
|
|
120
|
+
function engageIdentify(identity, consent) {
|
|
121
|
+
const e = resolve();
|
|
122
|
+
if (e && typeof e.identify === "function") e.identify(identity, consent);
|
|
123
|
+
else warnNotReady();
|
|
124
|
+
}
|
|
125
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
126
|
+
0 && (module.exports = {
|
|
127
|
+
EngageProvider,
|
|
128
|
+
engageIdentify,
|
|
129
|
+
sendEngageEvent
|
|
130
|
+
});
|
|
131
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/provider.tsx","../src/engage.ts"],"sourcesContent":["export { EngageProvider } from './provider'\nexport type { EngageProviderProps } from './provider'\n\nexport { sendEngageEvent, engageIdentify } from './engage'\nexport type { EngageIdentity, EngageConsent, EngageGlobal } from './engage'\n","import { useEffect } from 'react'\n\nexport interface EngageProviderProps {\n /** Your source public key, e.g. \"pk_live_...\". */\n publicKey: string\n /** Tracking host origin that serves engage.v1.js, e.g. \"https://engage.data360.asia\". */\n host: string\n /**\n * Auto-fire `page.viewed` on SPA route changes (default true). Works in Next\n * App/Pages Router and React Router without importing them — it hooks the\n * History API. Disable to fire page views yourself.\n */\n autoPageview?: boolean\n /** Optional branch id attached to every event. */\n branchId?: number | string\n children?: React.ReactNode\n}\n\nconst STUB_METHODS = ['init', 'track', 'identify', 'flush'] as const\nconst NAV_EVENT = 'engagex:navigation'\n\n/**\n * Mount once near the root of your app.\n *\n * // app/layout.tsx (Next.js) — it's a client component\n * <EngageProvider publicKey=\"pk_...\" host=\"https://engage.data360.asia\" />\n *\n * It injects engage.v1.js from `host` (auto-updates, keeps endpoint auto-detect\n * and shared _cdp_tid identity) and tracks page views across client navigation.\n */\nexport function EngageProvider({\n publicKey,\n host,\n autoPageview = true,\n branchId,\n children,\n}: EngageProviderProps) {\n useEffect(() => {\n if (typeof window === 'undefined' || !publicKey || !host) return\n const w = window as unknown as Record<string, any>\n const base = host.replace(/\\/+$/, '')\n\n // 1) Install the async-loader stub + inject engage.v1.js exactly once.\n if (!w.__engagex_loaded) {\n w.__engagex_loaded = true\n const name = 'engage'\n if (!w[name]) {\n w[name] = { _q: [] as unknown[] }\n STUB_METHODS.forEach((m) => {\n w[name][m] = function () {\n // eslint-disable-next-line prefer-rest-params\n w[name]._q.push([m, arguments])\n }\n })\n }\n const el = document.createElement('script')\n el.async = true\n el.src = base + '/engage.v1.js'\n const first = document.getElementsByTagName('script')[0]\n if (first && first.parentNode) first.parentNode.insertBefore(el, first)\n else document.head.appendChild(el)\n // autoPageview:false at SDK level — page views are handled here so SPA\n // route changes are captured (SDK only auto-fires once on init).\n w[name].init({ key: publicKey, endpoint: base, autoPageview: false, branch_id: branchId })\n }\n\n if (!autoPageview) return\n\n const fire = () => {\n try {\n w.engage?.track('page.viewed', { url: window.location.href })\n } catch {\n /* no-op */\n }\n }\n fire() // initial view\n\n // 2) Framework-agnostic SPA route tracking: patch History API once, then\n // listen for our synthetic nav event + popstate.\n const origPush = window.history.pushState\n const origReplace = window.history.replaceState\n let patchedHere = false\n if (!w.__engagex_history_patched) {\n w.__engagex_history_patched = true\n patchedHere = true\n window.history.pushState = function (this: History, ...args: unknown[]) {\n const r = origPush.apply(this, args as [unknown, string, (string | URL | null)?])\n window.dispatchEvent(new Event(NAV_EVENT))\n return r\n }\n window.history.replaceState = function (this: History, ...args: unknown[]) {\n const r = origReplace.apply(this, args as [unknown, string, (string | URL | null)?])\n window.dispatchEvent(new Event(NAV_EVENT))\n return r\n }\n }\n window.addEventListener(NAV_EVENT, fire)\n window.addEventListener('popstate', fire)\n\n return () => {\n window.removeEventListener(NAV_EVENT, fire)\n window.removeEventListener('popstate', fire)\n if (patchedHere) {\n window.history.pushState = origPush\n window.history.replaceState = origReplace\n w.__engagex_history_patched = false\n }\n }\n }, [publicKey, host, autoPageview, branchId])\n\n return <>{children}</>\n}\n","/**\n * Typed helpers to call the EngageX browser SDK (window.engage) safely from\n * React / Next.js. All calls are SSR-guarded — a no-op on the server.\n *\n * The actual tracking logic lives in engage.v1.js served from your tracking\n * host; <EngageProvider> injects it. These helpers just forward to it.\n */\n\nexport interface EngageIdentity {\n /** At least one resolving key is required: phone / email / zalo_id. */\n phone?: string\n email?: string\n zalo_id?: string\n /** Optional traits — applied fill-missing to the customer profile. */\n name?: string\n /** male | female | other (normalized server-side from \"Nam\"/\"Nữ\"...). */\n gender?: string\n /** e.g. 1990-12-31 or 31/12/1990. */\n birthday?: string\n address?: string\n [key: string]: unknown\n}\n\nexport interface EngageConsent {\n consent: boolean\n purpose?: string\n policy_version?: string\n}\n\nexport interface EngageGlobal {\n init: (opts: Record<string, unknown>) => void\n track: (event: string, properties?: Record<string, unknown>) => void\n identify: (identity: EngageIdentity, consent?: EngageConsent) => void\n flush?: (useBeacon?: boolean) => void\n anonId?: () => string\n _q?: unknown[]\n}\n\ndeclare global {\n interface Window {\n engage?: EngageGlobal\n }\n}\n\nfunction resolve(): EngageGlobal | undefined {\n if (typeof window === 'undefined') return undefined\n return window.engage\n}\n\nfunction warnNotReady(): void {\n if (typeof console !== 'undefined') {\n console.warn('[engagex] window.engage is not ready — did you mount <EngageProvider>?')\n }\n}\n\n/**\n * Send a business/behavioural event, e.g.\n * sendEngageEvent('order.placed', { order_id: 'DH1', total: 1990000 })\n */\nexport function sendEngageEvent(event: string, properties?: Record<string, unknown>): void {\n const e = resolve()\n if (e && typeof e.track === 'function') e.track(event, properties)\n else warnNotReady()\n}\n\n/**\n * Identify the visitor. `identity` mixes resolving keys (phone/email/zalo_id,\n * at least one) with optional traits (name/gender/birthday/address). Re-calling\n * with the same phone updates (fill-missing) — it never creates a duplicate.\n * engageIdentify({ phone: '09...', name: 'An' }, { consent: true })\n */\nexport function engageIdentify(identity: EngageIdentity, consent?: EngageConsent): void {\n const e = resolve()\n if (e && typeof e.identify === 'function') e.identify(identity, consent)\n else warnNotReady()\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
interface EngageProviderProps {
|
|
4
|
+
/** Your source public key, e.g. "pk_live_...". */
|
|
5
|
+
publicKey: string;
|
|
6
|
+
/** Tracking host origin that serves engage.v1.js, e.g. "https://engage.data360.asia". */
|
|
7
|
+
host: string;
|
|
8
|
+
/**
|
|
9
|
+
* Auto-fire `page.viewed` on SPA route changes (default true). Works in Next
|
|
10
|
+
* App/Pages Router and React Router without importing them — it hooks the
|
|
11
|
+
* History API. Disable to fire page views yourself.
|
|
12
|
+
*/
|
|
13
|
+
autoPageview?: boolean;
|
|
14
|
+
/** Optional branch id attached to every event. */
|
|
15
|
+
branchId?: number | string;
|
|
16
|
+
children?: React.ReactNode;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Mount once near the root of your app.
|
|
20
|
+
*
|
|
21
|
+
* // app/layout.tsx (Next.js) — it's a client component
|
|
22
|
+
* <EngageProvider publicKey="pk_..." host="https://engage.data360.asia" />
|
|
23
|
+
*
|
|
24
|
+
* It injects engage.v1.js from `host` (auto-updates, keeps endpoint auto-detect
|
|
25
|
+
* and shared _cdp_tid identity) and tracks page views across client navigation.
|
|
26
|
+
*/
|
|
27
|
+
declare function EngageProvider({ publicKey, host, autoPageview, branchId, children, }: EngageProviderProps): react.JSX.Element;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Typed helpers to call the EngageX browser SDK (window.engage) safely from
|
|
31
|
+
* React / Next.js. All calls are SSR-guarded — a no-op on the server.
|
|
32
|
+
*
|
|
33
|
+
* The actual tracking logic lives in engage.v1.js served from your tracking
|
|
34
|
+
* host; <EngageProvider> injects it. These helpers just forward to it.
|
|
35
|
+
*/
|
|
36
|
+
interface EngageIdentity {
|
|
37
|
+
/** At least one resolving key is required: phone / email / zalo_id. */
|
|
38
|
+
phone?: string;
|
|
39
|
+
email?: string;
|
|
40
|
+
zalo_id?: string;
|
|
41
|
+
/** Optional traits — applied fill-missing to the customer profile. */
|
|
42
|
+
name?: string;
|
|
43
|
+
/** male | female | other (normalized server-side from "Nam"/"Nữ"...). */
|
|
44
|
+
gender?: string;
|
|
45
|
+
/** e.g. 1990-12-31 or 31/12/1990. */
|
|
46
|
+
birthday?: string;
|
|
47
|
+
address?: string;
|
|
48
|
+
[key: string]: unknown;
|
|
49
|
+
}
|
|
50
|
+
interface EngageConsent {
|
|
51
|
+
consent: boolean;
|
|
52
|
+
purpose?: string;
|
|
53
|
+
policy_version?: string;
|
|
54
|
+
}
|
|
55
|
+
interface EngageGlobal {
|
|
56
|
+
init: (opts: Record<string, unknown>) => void;
|
|
57
|
+
track: (event: string, properties?: Record<string, unknown>) => void;
|
|
58
|
+
identify: (identity: EngageIdentity, consent?: EngageConsent) => void;
|
|
59
|
+
flush?: (useBeacon?: boolean) => void;
|
|
60
|
+
anonId?: () => string;
|
|
61
|
+
_q?: unknown[];
|
|
62
|
+
}
|
|
63
|
+
declare global {
|
|
64
|
+
interface Window {
|
|
65
|
+
engage?: EngageGlobal;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Send a business/behavioural event, e.g.
|
|
70
|
+
* sendEngageEvent('order.placed', { order_id: 'DH1', total: 1990000 })
|
|
71
|
+
*/
|
|
72
|
+
declare function sendEngageEvent(event: string, properties?: Record<string, unknown>): void;
|
|
73
|
+
/**
|
|
74
|
+
* Identify the visitor. `identity` mixes resolving keys (phone/email/zalo_id,
|
|
75
|
+
* at least one) with optional traits (name/gender/birthday/address). Re-calling
|
|
76
|
+
* with the same phone updates (fill-missing) — it never creates a duplicate.
|
|
77
|
+
* engageIdentify({ phone: '09...', name: 'An' }, { consent: true })
|
|
78
|
+
*/
|
|
79
|
+
declare function engageIdentify(identity: EngageIdentity, consent?: EngageConsent): void;
|
|
80
|
+
|
|
81
|
+
export { type EngageConsent, type EngageGlobal, type EngageIdentity, EngageProvider, type EngageProviderProps, engageIdentify, sendEngageEvent };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
interface EngageProviderProps {
|
|
4
|
+
/** Your source public key, e.g. "pk_live_...". */
|
|
5
|
+
publicKey: string;
|
|
6
|
+
/** Tracking host origin that serves engage.v1.js, e.g. "https://engage.data360.asia". */
|
|
7
|
+
host: string;
|
|
8
|
+
/**
|
|
9
|
+
* Auto-fire `page.viewed` on SPA route changes (default true). Works in Next
|
|
10
|
+
* App/Pages Router and React Router without importing them — it hooks the
|
|
11
|
+
* History API. Disable to fire page views yourself.
|
|
12
|
+
*/
|
|
13
|
+
autoPageview?: boolean;
|
|
14
|
+
/** Optional branch id attached to every event. */
|
|
15
|
+
branchId?: number | string;
|
|
16
|
+
children?: React.ReactNode;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Mount once near the root of your app.
|
|
20
|
+
*
|
|
21
|
+
* // app/layout.tsx (Next.js) — it's a client component
|
|
22
|
+
* <EngageProvider publicKey="pk_..." host="https://engage.data360.asia" />
|
|
23
|
+
*
|
|
24
|
+
* It injects engage.v1.js from `host` (auto-updates, keeps endpoint auto-detect
|
|
25
|
+
* and shared _cdp_tid identity) and tracks page views across client navigation.
|
|
26
|
+
*/
|
|
27
|
+
declare function EngageProvider({ publicKey, host, autoPageview, branchId, children, }: EngageProviderProps): react.JSX.Element;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Typed helpers to call the EngageX browser SDK (window.engage) safely from
|
|
31
|
+
* React / Next.js. All calls are SSR-guarded — a no-op on the server.
|
|
32
|
+
*
|
|
33
|
+
* The actual tracking logic lives in engage.v1.js served from your tracking
|
|
34
|
+
* host; <EngageProvider> injects it. These helpers just forward to it.
|
|
35
|
+
*/
|
|
36
|
+
interface EngageIdentity {
|
|
37
|
+
/** At least one resolving key is required: phone / email / zalo_id. */
|
|
38
|
+
phone?: string;
|
|
39
|
+
email?: string;
|
|
40
|
+
zalo_id?: string;
|
|
41
|
+
/** Optional traits — applied fill-missing to the customer profile. */
|
|
42
|
+
name?: string;
|
|
43
|
+
/** male | female | other (normalized server-side from "Nam"/"Nữ"...). */
|
|
44
|
+
gender?: string;
|
|
45
|
+
/** e.g. 1990-12-31 or 31/12/1990. */
|
|
46
|
+
birthday?: string;
|
|
47
|
+
address?: string;
|
|
48
|
+
[key: string]: unknown;
|
|
49
|
+
}
|
|
50
|
+
interface EngageConsent {
|
|
51
|
+
consent: boolean;
|
|
52
|
+
purpose?: string;
|
|
53
|
+
policy_version?: string;
|
|
54
|
+
}
|
|
55
|
+
interface EngageGlobal {
|
|
56
|
+
init: (opts: Record<string, unknown>) => void;
|
|
57
|
+
track: (event: string, properties?: Record<string, unknown>) => void;
|
|
58
|
+
identify: (identity: EngageIdentity, consent?: EngageConsent) => void;
|
|
59
|
+
flush?: (useBeacon?: boolean) => void;
|
|
60
|
+
anonId?: () => string;
|
|
61
|
+
_q?: unknown[];
|
|
62
|
+
}
|
|
63
|
+
declare global {
|
|
64
|
+
interface Window {
|
|
65
|
+
engage?: EngageGlobal;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Send a business/behavioural event, e.g.
|
|
70
|
+
* sendEngageEvent('order.placed', { order_id: 'DH1', total: 1990000 })
|
|
71
|
+
*/
|
|
72
|
+
declare function sendEngageEvent(event: string, properties?: Record<string, unknown>): void;
|
|
73
|
+
/**
|
|
74
|
+
* Identify the visitor. `identity` mixes resolving keys (phone/email/zalo_id,
|
|
75
|
+
* at least one) with optional traits (name/gender/birthday/address). Re-calling
|
|
76
|
+
* with the same phone updates (fill-missing) — it never creates a duplicate.
|
|
77
|
+
* engageIdentify({ phone: '09...', name: 'An' }, { consent: true })
|
|
78
|
+
*/
|
|
79
|
+
declare function engageIdentify(identity: EngageIdentity, consent?: EngageConsent): void;
|
|
80
|
+
|
|
81
|
+
export { type EngageConsent, type EngageGlobal, type EngageIdentity, EngageProvider, type EngageProviderProps, engageIdentify, sendEngageEvent };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/provider.tsx
|
|
4
|
+
import { useEffect } from "react";
|
|
5
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
6
|
+
var STUB_METHODS = ["init", "track", "identify", "flush"];
|
|
7
|
+
var NAV_EVENT = "engagex:navigation";
|
|
8
|
+
function EngageProvider({
|
|
9
|
+
publicKey,
|
|
10
|
+
host,
|
|
11
|
+
autoPageview = true,
|
|
12
|
+
branchId,
|
|
13
|
+
children
|
|
14
|
+
}) {
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (typeof window === "undefined" || !publicKey || !host) return;
|
|
17
|
+
const w = window;
|
|
18
|
+
const base = host.replace(/\/+$/, "");
|
|
19
|
+
if (!w.__engagex_loaded) {
|
|
20
|
+
w.__engagex_loaded = true;
|
|
21
|
+
const name = "engage";
|
|
22
|
+
if (!w[name]) {
|
|
23
|
+
w[name] = { _q: [] };
|
|
24
|
+
STUB_METHODS.forEach((m) => {
|
|
25
|
+
w[name][m] = function() {
|
|
26
|
+
w[name]._q.push([m, arguments]);
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const el = document.createElement("script");
|
|
31
|
+
el.async = true;
|
|
32
|
+
el.src = base + "/engage.v1.js";
|
|
33
|
+
const first = document.getElementsByTagName("script")[0];
|
|
34
|
+
if (first && first.parentNode) first.parentNode.insertBefore(el, first);
|
|
35
|
+
else document.head.appendChild(el);
|
|
36
|
+
w[name].init({ key: publicKey, endpoint: base, autoPageview: false, branch_id: branchId });
|
|
37
|
+
}
|
|
38
|
+
if (!autoPageview) return;
|
|
39
|
+
const fire = () => {
|
|
40
|
+
try {
|
|
41
|
+
w.engage?.track("page.viewed", { url: window.location.href });
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
fire();
|
|
46
|
+
const origPush = window.history.pushState;
|
|
47
|
+
const origReplace = window.history.replaceState;
|
|
48
|
+
let patchedHere = false;
|
|
49
|
+
if (!w.__engagex_history_patched) {
|
|
50
|
+
w.__engagex_history_patched = true;
|
|
51
|
+
patchedHere = true;
|
|
52
|
+
window.history.pushState = function(...args) {
|
|
53
|
+
const r = origPush.apply(this, args);
|
|
54
|
+
window.dispatchEvent(new Event(NAV_EVENT));
|
|
55
|
+
return r;
|
|
56
|
+
};
|
|
57
|
+
window.history.replaceState = function(...args) {
|
|
58
|
+
const r = origReplace.apply(this, args);
|
|
59
|
+
window.dispatchEvent(new Event(NAV_EVENT));
|
|
60
|
+
return r;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
window.addEventListener(NAV_EVENT, fire);
|
|
64
|
+
window.addEventListener("popstate", fire);
|
|
65
|
+
return () => {
|
|
66
|
+
window.removeEventListener(NAV_EVENT, fire);
|
|
67
|
+
window.removeEventListener("popstate", fire);
|
|
68
|
+
if (patchedHere) {
|
|
69
|
+
window.history.pushState = origPush;
|
|
70
|
+
window.history.replaceState = origReplace;
|
|
71
|
+
w.__engagex_history_patched = false;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}, [publicKey, host, autoPageview, branchId]);
|
|
75
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/engage.ts
|
|
79
|
+
function resolve() {
|
|
80
|
+
if (typeof window === "undefined") return void 0;
|
|
81
|
+
return window.engage;
|
|
82
|
+
}
|
|
83
|
+
function warnNotReady() {
|
|
84
|
+
if (typeof console !== "undefined") {
|
|
85
|
+
console.warn("[engagex] window.engage is not ready \u2014 did you mount <EngageProvider>?");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function sendEngageEvent(event, properties) {
|
|
89
|
+
const e = resolve();
|
|
90
|
+
if (e && typeof e.track === "function") e.track(event, properties);
|
|
91
|
+
else warnNotReady();
|
|
92
|
+
}
|
|
93
|
+
function engageIdentify(identity, consent) {
|
|
94
|
+
const e = resolve();
|
|
95
|
+
if (e && typeof e.identify === "function") e.identify(identity, consent);
|
|
96
|
+
else warnNotReady();
|
|
97
|
+
}
|
|
98
|
+
export {
|
|
99
|
+
EngageProvider,
|
|
100
|
+
engageIdentify,
|
|
101
|
+
sendEngageEvent
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/provider.tsx","../src/engage.ts"],"sourcesContent":["import { useEffect } from 'react'\n\nexport interface EngageProviderProps {\n /** Your source public key, e.g. \"pk_live_...\". */\n publicKey: string\n /** Tracking host origin that serves engage.v1.js, e.g. \"https://engage.data360.asia\". */\n host: string\n /**\n * Auto-fire `page.viewed` on SPA route changes (default true). Works in Next\n * App/Pages Router and React Router without importing them — it hooks the\n * History API. Disable to fire page views yourself.\n */\n autoPageview?: boolean\n /** Optional branch id attached to every event. */\n branchId?: number | string\n children?: React.ReactNode\n}\n\nconst STUB_METHODS = ['init', 'track', 'identify', 'flush'] as const\nconst NAV_EVENT = 'engagex:navigation'\n\n/**\n * Mount once near the root of your app.\n *\n * // app/layout.tsx (Next.js) — it's a client component\n * <EngageProvider publicKey=\"pk_...\" host=\"https://engage.data360.asia\" />\n *\n * It injects engage.v1.js from `host` (auto-updates, keeps endpoint auto-detect\n * and shared _cdp_tid identity) and tracks page views across client navigation.\n */\nexport function EngageProvider({\n publicKey,\n host,\n autoPageview = true,\n branchId,\n children,\n}: EngageProviderProps) {\n useEffect(() => {\n if (typeof window === 'undefined' || !publicKey || !host) return\n const w = window as unknown as Record<string, any>\n const base = host.replace(/\\/+$/, '')\n\n // 1) Install the async-loader stub + inject engage.v1.js exactly once.\n if (!w.__engagex_loaded) {\n w.__engagex_loaded = true\n const name = 'engage'\n if (!w[name]) {\n w[name] = { _q: [] as unknown[] }\n STUB_METHODS.forEach((m) => {\n w[name][m] = function () {\n // eslint-disable-next-line prefer-rest-params\n w[name]._q.push([m, arguments])\n }\n })\n }\n const el = document.createElement('script')\n el.async = true\n el.src = base + '/engage.v1.js'\n const first = document.getElementsByTagName('script')[0]\n if (first && first.parentNode) first.parentNode.insertBefore(el, first)\n else document.head.appendChild(el)\n // autoPageview:false at SDK level — page views are handled here so SPA\n // route changes are captured (SDK only auto-fires once on init).\n w[name].init({ key: publicKey, endpoint: base, autoPageview: false, branch_id: branchId })\n }\n\n if (!autoPageview) return\n\n const fire = () => {\n try {\n w.engage?.track('page.viewed', { url: window.location.href })\n } catch {\n /* no-op */\n }\n }\n fire() // initial view\n\n // 2) Framework-agnostic SPA route tracking: patch History API once, then\n // listen for our synthetic nav event + popstate.\n const origPush = window.history.pushState\n const origReplace = window.history.replaceState\n let patchedHere = false\n if (!w.__engagex_history_patched) {\n w.__engagex_history_patched = true\n patchedHere = true\n window.history.pushState = function (this: History, ...args: unknown[]) {\n const r = origPush.apply(this, args as [unknown, string, (string | URL | null)?])\n window.dispatchEvent(new Event(NAV_EVENT))\n return r\n }\n window.history.replaceState = function (this: History, ...args: unknown[]) {\n const r = origReplace.apply(this, args as [unknown, string, (string | URL | null)?])\n window.dispatchEvent(new Event(NAV_EVENT))\n return r\n }\n }\n window.addEventListener(NAV_EVENT, fire)\n window.addEventListener('popstate', fire)\n\n return () => {\n window.removeEventListener(NAV_EVENT, fire)\n window.removeEventListener('popstate', fire)\n if (patchedHere) {\n window.history.pushState = origPush\n window.history.replaceState = origReplace\n w.__engagex_history_patched = false\n }\n }\n }, [publicKey, host, autoPageview, branchId])\n\n return <>{children}</>\n}\n","/**\n * Typed helpers to call the EngageX browser SDK (window.engage) safely from\n * React / Next.js. All calls are SSR-guarded — a no-op on the server.\n *\n * The actual tracking logic lives in engage.v1.js served from your tracking\n * host; <EngageProvider> injects it. These helpers just forward to it.\n */\n\nexport interface EngageIdentity {\n /** At least one resolving key is required: phone / email / zalo_id. */\n phone?: string\n email?: string\n zalo_id?: string\n /** Optional traits — applied fill-missing to the customer profile. */\n name?: string\n /** male | female | other (normalized server-side from \"Nam\"/\"Nữ\"...). */\n gender?: string\n /** e.g. 1990-12-31 or 31/12/1990. */\n birthday?: string\n address?: string\n [key: string]: unknown\n}\n\nexport interface EngageConsent {\n consent: boolean\n purpose?: string\n policy_version?: string\n}\n\nexport interface EngageGlobal {\n init: (opts: Record<string, unknown>) => void\n track: (event: string, properties?: Record<string, unknown>) => void\n identify: (identity: EngageIdentity, consent?: EngageConsent) => void\n flush?: (useBeacon?: boolean) => void\n anonId?: () => string\n _q?: unknown[]\n}\n\ndeclare global {\n interface Window {\n engage?: EngageGlobal\n }\n}\n\nfunction resolve(): EngageGlobal | undefined {\n if (typeof window === 'undefined') return undefined\n return window.engage\n}\n\nfunction warnNotReady(): void {\n if (typeof console !== 'undefined') {\n console.warn('[engagex] window.engage is not ready — did you mount <EngageProvider>?')\n }\n}\n\n/**\n * Send a business/behavioural event, e.g.\n * sendEngageEvent('order.placed', { order_id: 'DH1', total: 1990000 })\n */\nexport function sendEngageEvent(event: string, properties?: Record<string, unknown>): void {\n const e = resolve()\n if (e && typeof e.track === 'function') e.track(event, properties)\n else warnNotReady()\n}\n\n/**\n * Identify the visitor. `identity` mixes resolving keys (phone/email/zalo_id,\n * at least one) with optional traits (name/gender/birthday/address). Re-calling\n * with the same phone updates (fill-missing) — it never creates a duplicate.\n * engageIdentify({ phone: '09...', name: 'An' }, { consent: true })\n */\nexport function engageIdentify(identity: EngageIdentity, consent?: EngageConsent): void {\n const e = resolve()\n if (e && typeof e.identify === 'function') e.identify(identity, consent)\n else warnNotReady()\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@engagex/react",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "React / Next.js wrapper for EngageX tracking. Loads engage.v1.js from your tracking host and gives you a typed <EngageProvider>, sendEngageEvent() and engageIdentify() with automatic SPA page-view tracking.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/index.cjs"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"react": ">=18"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/react": "^18.3.12",
|
|
31
|
+
"react": "^18.3.1",
|
|
32
|
+
"tsup": "^8.3.5",
|
|
33
|
+
"typescript": "^5.6.3"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"engagex",
|
|
40
|
+
"tracking",
|
|
41
|
+
"analytics",
|
|
42
|
+
"cdp",
|
|
43
|
+
"react",
|
|
44
|
+
"nextjs",
|
|
45
|
+
"next"
|
|
46
|
+
],
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18"
|
|
49
|
+
}
|
|
50
|
+
}
|