@g2crowd/buyer-intent-provider-sdk 0.3.0 → 0.4.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 +218 -194
- package/package.json +9 -14
- package/{browser → src/browser}/components/index.js +7 -2
- package/src/browser/components/tracker.js +105 -0
- package/src/browser/dom.js +184 -0
- package/{browser → src/browser}/elements/base.js +26 -1
- package/src/browser/elements/index.js +6 -0
- package/{browser → src/browser}/index.js +2 -0
- package/{browser → src/browser}/sdk.js +104 -30
- package/{index.d.ts → src/index.d.ts} +34 -5
- package/{index.js → src/index.js} +2 -0
- package/{react → src/react}/index.d.ts +23 -1
- package/{react → src/react}/index.js +8 -0
- package/browser/components/tracker.js +0 -40
- package/browser/dom.js +0 -98
- package/browser/elements/index.js +0 -1
- /package/{browser → src/browser}/core.js +0 -0
- /package/{browser → src/browser}/identity.js +0 -0
- /package/{browser → src/browser}/transport.js +0 -0
- /package/{server → src/server}/handlers.js +0 -0
- /package/{server → src/server}/index.js +0 -0
- /package/{server.js → src/server.js} +0 -0
package/README.md
CHANGED
|
@@ -1,193 +1,220 @@
|
|
|
1
1
|
# Buyer Intent JS SDK
|
|
2
2
|
|
|
3
|
-
Buyer intent tracking
|
|
4
|
-
|
|
5
|
-
## Install
|
|
3
|
+
Buyer intent tracking for partner sites. Drop custom HTML elements into any page — no framework required. Events are sent via `sendBeacon` for reliable delivery during navigation.
|
|
6
4
|
|
|
7
5
|
```bash
|
|
8
6
|
npm install @g2crowd/buyer-intent-provider-sdk
|
|
9
7
|
```
|
|
10
8
|
|
|
11
|
-
## Entry Points
|
|
12
|
-
|
|
13
|
-
| Import Path | Contains | React Required |
|
|
14
|
-
| ------------------------------------------- | -------------------- | -------------- |
|
|
15
|
-
| `@g2crowd/buyer-intent-provider-sdk` | SDK, custom elements | No |
|
|
16
|
-
| `@g2crowd/buyer-intent-provider-sdk/react` | React components | Yes |
|
|
17
|
-
| `@g2crowd/buyer-intent-provider-sdk/server` | Server handlers | No |
|
|
18
|
-
|
|
19
9
|
## Quick Start
|
|
20
10
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
The SDK registers two custom elements — `<buyer-intent-view>` and `<buyer-intent-click>` — that work in any HTML page. No React, no build step required.
|
|
11
|
+
Import the SDK, wrap your page in a session, and you're tracking:
|
|
24
12
|
|
|
25
13
|
```html
|
|
26
14
|
<script type="module">
|
|
27
15
|
import '@g2crowd/buyer-intent-provider-sdk';
|
|
28
16
|
</script>
|
|
29
17
|
|
|
30
|
-
<buyer-intent-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
data-origin="g2.com"
|
|
34
|
-
data-activity-endpoint="/api/activity/events"
|
|
18
|
+
<buyer-intent-session
|
|
19
|
+
origin="yoursite.com"
|
|
20
|
+
activity-endpoint="/api/activity/events"
|
|
35
21
|
>
|
|
36
|
-
<h1>Acme CRM</h1>
|
|
37
|
-
<p>Product details here.</p>
|
|
38
22
|
|
|
39
|
-
<buyer-intent-
|
|
40
|
-
|
|
41
|
-
product-id="123"
|
|
42
|
-
data-origin="g2.com"
|
|
43
|
-
data-activity-endpoint="/api/activity/events"
|
|
44
|
-
>
|
|
45
|
-
<button>Get a Demo</button>
|
|
46
|
-
</buyer-intent-click>
|
|
47
|
-
</buyer-intent-view>
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### React Components
|
|
23
|
+
<buyer-intent-view tag="products.show">
|
|
24
|
+
<buyer-intent-subject product-id="123"></buyer-intent-subject>
|
|
51
25
|
|
|
52
|
-
|
|
26
|
+
<h1>Acme CRM</h1>
|
|
27
|
+
<p>Product details here.</p>
|
|
53
28
|
|
|
54
|
-
|
|
55
|
-
|
|
29
|
+
<buyer-intent-click event-name="/leads/create">
|
|
30
|
+
<button>Get a Demo</button>
|
|
31
|
+
</buyer-intent-click>
|
|
32
|
+
</buyer-intent-view>
|
|
56
33
|
|
|
57
|
-
|
|
58
|
-
return (
|
|
59
|
-
<BuyerIntent.ProfileView
|
|
60
|
-
productId={123}
|
|
61
|
-
origin="g2.com"
|
|
62
|
-
activityEndpoint="/api/activity/events"
|
|
63
|
-
>
|
|
64
|
-
<h1>Acme CRM</h1>
|
|
65
|
-
<p>Product details here.</p>
|
|
66
|
-
|
|
67
|
-
<BuyerIntent.LeadCreateClick productId={123}>
|
|
68
|
-
<button>Get a Demo</button>
|
|
69
|
-
</BuyerIntent.LeadCreateClick>
|
|
70
|
-
</BuyerIntent.ProfileView>
|
|
71
|
-
);
|
|
72
|
-
}
|
|
34
|
+
</buyer-intent-session>
|
|
73
35
|
```
|
|
74
36
|
|
|
75
|
-
|
|
37
|
+
That's it. When the page loads, a `$view` event fires with `product_ids: [123]` and `tag: "products.show"`. When the user clicks "Get a Demo", a `/leads/create` event fires. Both events inherit the session's `origin` and `activity-endpoint` automatically.
|
|
76
38
|
|
|
77
|
-
|
|
39
|
+
## Elements
|
|
78
40
|
|
|
79
|
-
|
|
80
|
-
import { BuyerIntent } from '@g2crowd/buyer-intent-provider-sdk/react';
|
|
41
|
+
### `<buyer-intent-session>`
|
|
81
42
|
|
|
82
|
-
|
|
83
|
-
productId={123}
|
|
84
|
-
origin="g2.com"
|
|
85
|
-
activityEndpoint="/api/activity/events"
|
|
86
|
-
>
|
|
87
|
-
<h1>Pricing</h1>
|
|
43
|
+
Wraps a page (or your entire app) and provides config to all elements inside it. Does not fire any events itself.
|
|
88
44
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
<button>Start Free Trial</button>
|
|
96
|
-
</buyer-intent-click>
|
|
97
|
-
</BuyerIntent.PricingView>;
|
|
98
|
-
```
|
|
45
|
+
| Attribute | Description |
|
|
46
|
+
| -------------------- | -------------------------------------------- |
|
|
47
|
+
| `origin` | Partner hostname |
|
|
48
|
+
| `activity-endpoint` | URL to POST events to |
|
|
49
|
+
| `user-type` | Visitor role (default: `"standard"`) |
|
|
50
|
+
| `distinct-id` | Override the auto-generated visitor ID |
|
|
99
51
|
|
|
100
|
-
|
|
52
|
+
| User Type | Meaning |
|
|
53
|
+
| ---------------- | ------------------------------------------------- |
|
|
54
|
+
| `guest` | Logged-out visitor |
|
|
55
|
+
| `standard` | Logged-in user (default) |
|
|
56
|
+
| `vendor-admin` | Vendor role viewing their own content |
|
|
57
|
+
| `observer` | Internal employee / superuser |
|
|
101
58
|
|
|
102
|
-
|
|
59
|
+
```html
|
|
60
|
+
<buyer-intent-session origin="yoursite.com" activity-endpoint="/api/activity/events">
|
|
61
|
+
<!-- everything inside inherits origin and endpoint -->
|
|
62
|
+
</buyer-intent-session>
|
|
63
|
+
```
|
|
103
64
|
|
|
104
65
|
### `<buyer-intent-view>`
|
|
105
66
|
|
|
106
67
|
Fires a `$view` event when the element connects to the DOM. One event per page navigation.
|
|
107
68
|
|
|
108
|
-
| Attribute
|
|
109
|
-
|
|
|
110
|
-
| `tag`
|
|
111
|
-
| `
|
|
112
|
-
| `product-
|
|
113
|
-
| `category-id`
|
|
69
|
+
| Attribute | Description |
|
|
70
|
+
| ----------------- | ------------------------------------------- |
|
|
71
|
+
| `tag` | View type identifier (optional) |
|
|
72
|
+
| `source-location` | Controller/action string (optional) |
|
|
73
|
+
| `product-id` | Single product ID (simple pages) |
|
|
74
|
+
| `category-id` | Single category ID (simple pages) |
|
|
114
75
|
|
|
115
|
-
|
|
76
|
+
For pages with one product, you can put the ID directly on the view:
|
|
116
77
|
|
|
117
78
|
```html
|
|
118
|
-
<buyer-intent-view
|
|
119
|
-
|
|
120
|
-
category-id="45"
|
|
121
|
-
data-origin="g2.com"
|
|
122
|
-
data-activity-endpoint="/api/activity/events"
|
|
123
|
-
>
|
|
124
|
-
<h1>CRM Software</h1>
|
|
79
|
+
<buyer-intent-view tag="products.show" product-id="123">
|
|
80
|
+
<h1>Acme CRM</h1>
|
|
125
81
|
</buyer-intent-view>
|
|
126
82
|
```
|
|
127
83
|
|
|
128
|
-
|
|
84
|
+
For pages with multiple products, use `<buyer-intent-subject>` children instead (see below).
|
|
85
|
+
|
|
86
|
+
### `<buyer-intent-subject>`
|
|
87
|
+
|
|
88
|
+
Declares that a product or category is a **subject** of the current view. Nest any number of these inside a `<buyer-intent-view>` — their IDs accumulate into the view's event automatically. Does not render anything visible.
|
|
89
|
+
|
|
90
|
+
| Attribute | Description |
|
|
91
|
+
| -------------- | ---------------------------------------------------- |
|
|
92
|
+
| `product-id` | Single product ID |
|
|
93
|
+
| `product-ids` | Multiple product IDs (JSON array or comma-separated) |
|
|
94
|
+
| `category-id` | Single category ID |
|
|
95
|
+
| `category-ids` | Multiple category IDs (JSON array or comma-separated)|
|
|
96
|
+
|
|
97
|
+
A comparison page that references two products:
|
|
129
98
|
|
|
130
99
|
```html
|
|
131
|
-
<buyer-intent-view tag="comparisons.show"
|
|
132
|
-
|
|
100
|
+
<buyer-intent-view tag="comparisons.show">
|
|
101
|
+
<buyer-intent-subject product-id="101"></buyer-intent-subject>
|
|
102
|
+
<buyer-intent-subject product-id="201"></buyer-intent-subject>
|
|
103
|
+
|
|
104
|
+
<h1>Acme CRM vs. Rival CRM</h1>
|
|
133
105
|
</buyer-intent-view>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The resulting event has `product_ids: [101, 201]`. If the view also has an inline `product-id`, they merge together without duplicates.
|
|
134
109
|
|
|
135
|
-
|
|
136
|
-
|
|
110
|
+
Subjects can live anywhere inside the view — they don't need to be direct children. This makes them easy to place next to the content they describe:
|
|
111
|
+
|
|
112
|
+
```html
|
|
113
|
+
<buyer-intent-view tag="categories.show">
|
|
114
|
+
<buyer-intent-subject category-id="45"></buyer-intent-subject>
|
|
115
|
+
<h1>CRM Software</h1>
|
|
116
|
+
|
|
117
|
+
<div class="product-grid">
|
|
118
|
+
<div class="product-card">
|
|
119
|
+
<buyer-intent-subject product-id="101"></buyer-intent-subject>
|
|
120
|
+
<h2>Acme CRM</h2>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div class="product-card">
|
|
124
|
+
<buyer-intent-subject product-id="201"></buyer-intent-subject>
|
|
125
|
+
<h2>Rival CRM</h2>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
137
128
|
</buyer-intent-view>
|
|
138
129
|
```
|
|
139
130
|
|
|
140
|
-
|
|
131
|
+
This fires one event: `category_ids: [45]`, `product_ids: [101, 201]`, `tag: "categories.show"`.
|
|
141
132
|
|
|
142
|
-
|
|
133
|
+
### `<buyer-intent-click>`
|
|
143
134
|
|
|
144
|
-
|
|
145
|
-
| ------------ | -------- | ----------------------------------- |
|
|
146
|
-
| `event-name` | Yes | Click event type (see values below) |
|
|
147
|
-
| `product-id` | - | Product ID |
|
|
135
|
+
Fires an event when the user clicks anything inside it.
|
|
148
136
|
|
|
149
|
-
|
|
137
|
+
| Attribute | Description |
|
|
138
|
+
| ------------ | ------------------------ |
|
|
139
|
+
| `event-name` | Click event type |
|
|
150
140
|
|
|
151
141
|
```html
|
|
152
|
-
<buyer-intent-click
|
|
153
|
-
event-name="/leads/create"
|
|
154
|
-
product-id="123"
|
|
155
|
-
data-origin="g2.com"
|
|
156
|
-
data-activity-endpoint="/api/activity/events"
|
|
157
|
-
>
|
|
142
|
+
<buyer-intent-click event-name="/leads/create">
|
|
158
143
|
<button>Get a Demo</button>
|
|
159
144
|
</buyer-intent-click>
|
|
160
145
|
```
|
|
161
146
|
|
|
162
|
-
|
|
147
|
+
A click element inside a view automatically inherits the view's tag, product IDs, and category IDs. A click inside a session inherits the session's config. You don't need to repeat anything.
|
|
163
148
|
|
|
164
|
-
|
|
149
|
+
## Full Example
|
|
165
150
|
|
|
166
|
-
|
|
167
|
-
| ------------------------ | ------------------------------------------------------ |
|
|
168
|
-
| `data-origin` | Partner hostname |
|
|
169
|
-
| `data-activity-endpoint` | URL to POST events to |
|
|
170
|
-
| `data-user-type` | User type string (default: `"standard"`) |
|
|
171
|
-
| `data-distinct-id` | Override the auto-generated visitor ID |
|
|
172
|
-
| `data-buyer-intent` | JSON object with `sourceLocation` and `context` fields |
|
|
151
|
+
Putting it all together — a product page with a session, view, subjects, and two click actions:
|
|
173
152
|
|
|
174
|
-
|
|
153
|
+
```html
|
|
154
|
+
<script type="module">
|
|
155
|
+
import '@g2crowd/buyer-intent-provider-sdk';
|
|
156
|
+
</script>
|
|
175
157
|
|
|
176
|
-
|
|
158
|
+
<buyer-intent-session
|
|
159
|
+
origin="yoursite.com"
|
|
160
|
+
activity-endpoint="/api/activity/events"
|
|
161
|
+
user-type="standard"
|
|
162
|
+
>
|
|
163
|
+
|
|
164
|
+
<buyer-intent-view
|
|
165
|
+
tag="products.show"
|
|
166
|
+
source-location="ProductsController#show"
|
|
167
|
+
>
|
|
168
|
+
<buyer-intent-subject product-id="123"></buyer-intent-subject>
|
|
169
|
+
<buyer-intent-subject category-id="45"></buyer-intent-subject>
|
|
170
|
+
|
|
171
|
+
<h1>Acme CRM</h1>
|
|
172
|
+
<p>The best CRM for small teams.</p>
|
|
173
|
+
|
|
174
|
+
<buyer-intent-click event-name="/leads/create">
|
|
175
|
+
<button>Get a Demo</button>
|
|
176
|
+
</buyer-intent-click>
|
|
177
|
+
|
|
178
|
+
<buyer-intent-click event-name="/ad/clicked">
|
|
179
|
+
<a href="https://acme.example.com">Visit Acme</a>
|
|
180
|
+
</buyer-intent-click>
|
|
181
|
+
</buyer-intent-view>
|
|
182
|
+
|
|
183
|
+
</buyer-intent-session>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## React Support
|
|
187
|
+
|
|
188
|
+
Import from `/react` and use the same element hierarchy with camelCase props:
|
|
177
189
|
|
|
178
190
|
```tsx
|
|
179
|
-
|
|
180
|
-
<buyer-intent-view tag="products.show" product-id="123">
|
|
191
|
+
import { BuyerIntent } from '@g2crowd/buyer-intent-provider-sdk/react';
|
|
181
192
|
|
|
182
|
-
|
|
183
|
-
|
|
193
|
+
export default function ProductPage() {
|
|
194
|
+
return (
|
|
195
|
+
<BuyerIntent.Session origin="yoursite.com" activityEndpoint="/api/activity/events">
|
|
196
|
+
<BuyerIntent.View tag="products.show" sourceLocation="ProductsController#show">
|
|
197
|
+
<BuyerIntent.Subject productId={123} />
|
|
198
|
+
<BuyerIntent.Subject categoryId={45} />
|
|
199
|
+
|
|
200
|
+
<h1>Acme CRM</h1>
|
|
201
|
+
|
|
202
|
+
<BuyerIntent.Click eventName="/leads/create">
|
|
203
|
+
<button>Get a Demo</button>
|
|
204
|
+
</BuyerIntent.Click>
|
|
205
|
+
</BuyerIntent.View>
|
|
206
|
+
</BuyerIntent.Session>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
184
209
|
```
|
|
185
210
|
|
|
186
|
-
|
|
211
|
+
`BuyerIntent.Session`, `BuyerIntent.View`, `BuyerIntent.Subject`, and `BuyerIntent.Click` map 1:1 to the HTML elements described above. The session is a good fit for a layout component or `_app` wrapper so it's set once for every page.
|
|
212
|
+
|
|
213
|
+
## Syntactic Sugar
|
|
187
214
|
|
|
188
|
-
|
|
215
|
+
For common page types, pre-built components hardcode the `tag` or `event-name` so you don't have to:
|
|
189
216
|
|
|
190
|
-
###
|
|
217
|
+
### Views
|
|
191
218
|
|
|
192
219
|
| Component | Tag | Required Prop |
|
|
193
220
|
| ----------------- | ----------------------- | ------------- |
|
|
@@ -198,62 +225,28 @@ Named React components wrap the custom elements with hardcoded `tag`/`event-name
|
|
|
198
225
|
| `CompareView` | `comparisons.show` | `productIds` |
|
|
199
226
|
| `WriteReviewView` | `reviewers.take_survey` | `productId` |
|
|
200
227
|
|
|
201
|
-
###
|
|
228
|
+
### Clicks
|
|
202
229
|
|
|
203
230
|
| Component | Event Name | Required Prop |
|
|
204
231
|
| ----------------- | --------------- | ------------- |
|
|
205
232
|
| `AdClick` | `/ad/clicked` | `productId` |
|
|
206
233
|
| `LeadCreateClick` | `/leads/create` | `productId` |
|
|
207
234
|
|
|
208
|
-
### Generic Trackers
|
|
209
|
-
|
|
210
|
-
For custom interaction types, use `ViewTracker` or `ClickTracker` directly:
|
|
211
|
-
|
|
212
235
|
```tsx
|
|
213
236
|
import { BuyerIntent } from '@g2crowd/buyer-intent-provider-sdk/react';
|
|
214
237
|
|
|
215
|
-
<BuyerIntent.
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
origin="g2.com"
|
|
219
|
-
activityEndpoint="/api/activity/events"
|
|
220
|
-
>
|
|
221
|
-
<CustomPage />
|
|
222
|
-
</BuyerIntent.ViewTracker>
|
|
238
|
+
<BuyerIntent.Session origin="yoursite.com" activityEndpoint="/api/activity/events">
|
|
239
|
+
<BuyerIntent.ProfileView productId={123}>
|
|
240
|
+
<h1>Acme CRM</h1>
|
|
223
241
|
|
|
224
|
-
<BuyerIntent.
|
|
225
|
-
|
|
226
|
-
</BuyerIntent.
|
|
242
|
+
<BuyerIntent.LeadCreateClick productId={123}>
|
|
243
|
+
<button>Get a Demo</button>
|
|
244
|
+
</BuyerIntent.LeadCreateClick>
|
|
245
|
+
</BuyerIntent.ProfileView>
|
|
246
|
+
</BuyerIntent.Session>
|
|
227
247
|
```
|
|
228
248
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
| Prop | Description |
|
|
232
|
-
| ------------------ | ---------------------------------------- |
|
|
233
|
-
| `origin` | Partner hostname |
|
|
234
|
-
| `activityEndpoint` | URL to POST events to |
|
|
235
|
-
| `userType` | User type string (default: `"standard"`) |
|
|
236
|
-
| `distinctId` | Override the auto-generated visitor ID |
|
|
237
|
-
| `sourceLocation` | Controller/action identifier |
|
|
238
|
-
| `context` | Arbitrary metadata object |
|
|
239
|
-
|
|
240
|
-
## Visit Properties
|
|
241
|
-
|
|
242
|
-
```js
|
|
243
|
-
import { buyerIntent } from '@g2crowd/buyer-intent-provider-sdk';
|
|
244
|
-
|
|
245
|
-
buyerIntent.setVisitProperties({
|
|
246
|
-
ip: '203.0.113.10',
|
|
247
|
-
referrer: 'https://example.com/',
|
|
248
|
-
landing_page: 'https://example.com/products/abc',
|
|
249
|
-
user_agent: 'Mozilla/5.0',
|
|
250
|
-
utm_source: 'newsletter',
|
|
251
|
-
utm_medium: 'email',
|
|
252
|
-
utm_campaign: 'spring_launch',
|
|
253
|
-
utm_term: 'buyer-intent',
|
|
254
|
-
utm_content: 'cta-button',
|
|
255
|
-
});
|
|
256
|
-
```
|
|
249
|
+
These components still accept `origin`, `activityEndpoint`, `sourceLocation`, and `context` props for cases where you aren't using a session wrapper.
|
|
257
250
|
|
|
258
251
|
## Backend
|
|
259
252
|
|
|
@@ -314,50 +307,81 @@ const POST = createNextRouteHandler({
|
|
|
314
307
|
export { POST };
|
|
315
308
|
```
|
|
316
309
|
|
|
317
|
-
##
|
|
318
|
-
|
|
319
|
-
| Function | Purpose |
|
|
320
|
-
| ---------------------------------- | ---------------------------------------------------------------------------------- |
|
|
321
|
-
| `createActivityHandler(options)` | Framework-agnostic handler that validates, enriches, and publishes events to Kafka |
|
|
322
|
-
| `createNextRouteHandler(options)` | Next.js Route Handler wrapper around `createActivityHandler` |
|
|
323
|
-
| `createDevLoggerProducer(options)` | Dev-only producer that logs messages instead of sending to Kafka |
|
|
324
|
-
| `topicName(options)` | Builds a topic name from `partnerId` and optional `prefix` |
|
|
325
|
-
|
|
326
|
-
## Kafka Setup (Partner Checklist)
|
|
310
|
+
## Event Payload
|
|
327
311
|
|
|
328
|
-
|
|
329
|
-
- Topic name (scoped to their tenant, default: `intent_events_<partnerId>`)
|
|
330
|
-
- Auth mechanism (SASL user/pass or mTLS)
|
|
331
|
-
- TLS requirements (on/off, CA certs if needed)
|
|
312
|
+
The client sends a beacon to the activity endpoint. The server handler wraps it into a composite event and writes it to Kafka.
|
|
332
313
|
|
|
333
|
-
|
|
314
|
+
### Client → Server (sendBeacon body)
|
|
334
315
|
|
|
335
316
|
```json
|
|
336
317
|
{
|
|
337
318
|
"name": "$view",
|
|
338
319
|
"properties": {
|
|
339
320
|
"product_ids": [123],
|
|
340
|
-
"category_ids": [
|
|
321
|
+
"category_ids": [45],
|
|
341
322
|
"tag": "products.show",
|
|
342
|
-
"url": "https://
|
|
323
|
+
"url": "https://yoursite.com/products/acme-crm",
|
|
343
324
|
"user_type": "standard",
|
|
344
325
|
"distinct_id": "visitor-uuid",
|
|
345
|
-
"origin": "
|
|
326
|
+
"origin": "yoursite.com",
|
|
346
327
|
"source_location": "ProductsController#show",
|
|
347
|
-
"context": {
|
|
328
|
+
"context": {}
|
|
348
329
|
},
|
|
349
330
|
"visit": {
|
|
350
331
|
"properties": {
|
|
351
|
-
"landing_page": "https://
|
|
352
|
-
"referrer": "https://
|
|
353
|
-
"user_agent": "Mozilla/5.0",
|
|
354
|
-
"ip": "203.0.113.10",
|
|
332
|
+
"landing_page": "https://yoursite.com/products/acme-crm",
|
|
333
|
+
"referrer": "https://google.com/",
|
|
334
|
+
"user_agent": "Mozilla/5.0 ...",
|
|
355
335
|
"utm_source": "newsletter",
|
|
356
336
|
"utm_medium": "email",
|
|
357
|
-
"utm_campaign": "
|
|
358
|
-
"utm_term": "
|
|
359
|
-
"utm_content": "cta
|
|
337
|
+
"utm_campaign": "spring-2026",
|
|
338
|
+
"utm_term": "crm+software",
|
|
339
|
+
"utm_content": "hero-cta"
|
|
360
340
|
}
|
|
361
341
|
}
|
|
362
342
|
}
|
|
363
343
|
```
|
|
344
|
+
|
|
345
|
+
### Server → Kafka (composite event)
|
|
346
|
+
|
|
347
|
+
The server enriches the client payload with server-side tokens, timestamps, and IP before writing to Kafka. This is the shape written to the `intent_events_{partnerId}` topic:
|
|
348
|
+
|
|
349
|
+
```json
|
|
350
|
+
{
|
|
351
|
+
"event": {
|
|
352
|
+
"id": "e0c1f2a3-...",
|
|
353
|
+
"name": "$view",
|
|
354
|
+
"time": "2026-02-17T14:20:00.000Z",
|
|
355
|
+
"properties": {
|
|
356
|
+
"product_ids": [123],
|
|
357
|
+
"category_ids": [45],
|
|
358
|
+
"tag": "products.show",
|
|
359
|
+
"url": "https://yoursite.com/products/acme-crm",
|
|
360
|
+
"user_type": "standard",
|
|
361
|
+
"distinct_id": "visitor-uuid",
|
|
362
|
+
"origin": "yoursite.com",
|
|
363
|
+
"source_location": "ProductsController#show",
|
|
364
|
+
"context": {}
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
"visit": {
|
|
368
|
+
"visit_token": "a1b2c3d4-...",
|
|
369
|
+
"visitor_token": "f5e6d7c8-...",
|
|
370
|
+
"started_at": "2026-02-17T14:20:00.000Z",
|
|
371
|
+
"created_at": "2026-02-17T14:20:00.000Z",
|
|
372
|
+
"properties": {
|
|
373
|
+
"landing_page": "https://yoursite.com/products/acme-crm",
|
|
374
|
+
"referrer": "https://google.com/",
|
|
375
|
+
"user_agent": "Mozilla/5.0 ...",
|
|
376
|
+
"ip": "203.0.113.42",
|
|
377
|
+
"utm_source": "newsletter",
|
|
378
|
+
"utm_medium": "email",
|
|
379
|
+
"utm_campaign": "spring-2026",
|
|
380
|
+
"utm_term": "crm+software",
|
|
381
|
+
"utm_content": "hero-cta"
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
The server sets `visit_token` and `visitor_token` as `httpOnly` cookies so repeat visits from the same browser share stable tokens.
|
package/package.json
CHANGED
|
@@ -1,29 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@g2crowd/buyer-intent-provider-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Buyer intent tracking SDK with pageview defaults",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "index.js",
|
|
6
|
+
"main": "src/index.js",
|
|
7
7
|
"exports": {
|
|
8
|
-
".": "./index.js",
|
|
9
|
-
"./react": "./react/index.js",
|
|
10
|
-
"./server": "./server/index.js"
|
|
8
|
+
".": "./src/index.js",
|
|
9
|
+
"./react": "./src/react/index.js",
|
|
10
|
+
"./server": "./src/server/index.js"
|
|
11
11
|
},
|
|
12
|
-
"types": "index.d.ts",
|
|
12
|
+
"types": "src/index.d.ts",
|
|
13
13
|
"files": [
|
|
14
|
-
"
|
|
15
|
-
"index.d.ts",
|
|
16
|
-
"browser",
|
|
17
|
-
"react",
|
|
18
|
-
"server.js",
|
|
19
|
-
"server",
|
|
14
|
+
"src",
|
|
20
15
|
"README.md"
|
|
21
16
|
],
|
|
22
17
|
"scripts": {
|
|
23
18
|
"test": "node --experimental-vm-modules ./node_modules/.bin/jest --config ./jest.config.cjs"
|
|
24
19
|
},
|
|
25
20
|
"bin": {
|
|
26
|
-
"buyer-intent-server": "server.js"
|
|
21
|
+
"buyer-intent-server": "src/server.js"
|
|
27
22
|
},
|
|
28
23
|
"license": "MIT",
|
|
29
24
|
"repository": {
|
|
@@ -46,7 +41,7 @@
|
|
|
46
41
|
}
|
|
47
42
|
},
|
|
48
43
|
"sideEffects": [
|
|
49
|
-
"./browser/elements/*.js"
|
|
44
|
+
"./src/browser/elements/*.js"
|
|
50
45
|
],
|
|
51
46
|
"devDependencies": {
|
|
52
47
|
"@testing-library/dom": "^9.3.4",
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
export {
|
|
2
|
-
|
|
1
|
+
export {
|
|
2
|
+
SessionProvider,
|
|
3
|
+
SubjectTracker,
|
|
4
|
+
ViewTracker,
|
|
5
|
+
ClickTracker,
|
|
6
|
+
} from './tracker.js';
|
|
7
|
+
import { SessionProvider, SubjectTracker, ViewTracker, ClickTracker } from './tracker.js';
|
|
3
8
|
import React from 'react';
|
|
4
9
|
|
|
5
10
|
function view(tag) {
|