@splendidlabz/tracking 0.2.10 → 0.2.12
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/lib/fb/node.js +164 -17
- package/lib/fb/readme.md +157 -19
- package/lib/fb/web.js +65 -8
- package/lib/posthog/node.js +3 -3
- package/lib/posthog/readme.md +8 -0
- package/lib/posthog/web.js +5 -2
- package/package.json +1 -1
package/lib/fb/node.js
CHANGED
|
@@ -8,24 +8,71 @@ import {
|
|
|
8
8
|
} from './consts.js'
|
|
9
9
|
import { formatUserData } from './utils.js'
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Creates a Facebook Conversions API tracker for server-side tracking
|
|
13
|
+
* @param {Object} config - Configuration options
|
|
14
|
+
* @param {string} config.pixelID - Facebook Pixel ID
|
|
15
|
+
* @param {string} [config.testEventCode] - Test event code for debugging
|
|
16
|
+
* @param {string} config.accessToken - Facebook Access Token (required)
|
|
17
|
+
* @param {string} config.framework - Framework being used (astro, express, fastify, koa)
|
|
18
|
+
* @return {Object} FB tracking instance
|
|
19
|
+
* @property {Function} sendEvent - Send a pre-formatted event to CAPI
|
|
20
|
+
* @property {Function} createEvent - Create an event payload
|
|
21
|
+
* @property {Function} track - Track an event and send to CAPI
|
|
22
|
+
* @property {Function} sendCAPIRequest - Send raw request to CAPI
|
|
23
|
+
* @see https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/
|
|
24
|
+
*/
|
|
12
25
|
export default function FB({ pixelID, testEventCode, accessToken, framework }) {
|
|
13
26
|
framework = framework.toLowerCase()
|
|
14
27
|
|
|
15
28
|
if (!accessToken) throw new Error('Facebook Access Token required')
|
|
16
29
|
|
|
17
30
|
return {
|
|
18
|
-
|
|
19
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Sends a pre-formatted event to Facebook CAPI
|
|
33
|
+
* Used when frontend has already prepared the event data
|
|
34
|
+
* @param {Object} [props={}] - Event properties including pixel_id, test_event_code, user_data
|
|
35
|
+
* @param {Object} [options={}] - Additional options
|
|
36
|
+
* @param {Object} [options.context] - Framework-specific request context
|
|
37
|
+
* @param {boolean} [options.debug] - Enable debug logging
|
|
38
|
+
* @param {boolean} [options.dryRun] - Skip actual API call
|
|
39
|
+
* @return {Promise<Object>} CAPI response
|
|
40
|
+
*/
|
|
20
41
|
async sendEvent(props = {}, options = {}) {
|
|
21
|
-
const { pixel_id, test_event_code, ...rest } = props
|
|
42
|
+
const { pixel_id, test_event_code, user_data, ...rest } = props
|
|
43
|
+
const { context } = options
|
|
44
|
+
|
|
45
|
+
const {
|
|
46
|
+
p: { fbc, fbp },
|
|
47
|
+
o: userProps,
|
|
48
|
+
} = splitObject(user_data, ['fbc', 'fbp'])
|
|
49
|
+
|
|
50
|
+
const data = {
|
|
51
|
+
...rest,
|
|
52
|
+
user_data: await normalizeUserData({
|
|
53
|
+
context,
|
|
54
|
+
framework,
|
|
55
|
+
userProps,
|
|
56
|
+
fbc,
|
|
57
|
+
fbp,
|
|
58
|
+
}),
|
|
59
|
+
}
|
|
60
|
+
|
|
22
61
|
const body = {
|
|
23
|
-
data: [omitEmpty(
|
|
62
|
+
data: [omitEmpty(data)],
|
|
24
63
|
test_event_code: test_event_code || testEventCode,
|
|
25
64
|
}
|
|
26
65
|
return this.sendCAPIRequest(body, { ...options, pixel_id })
|
|
27
66
|
},
|
|
28
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Creates a Facebook CAPI event payload
|
|
70
|
+
* @param {string} eventName - Name of the event to track
|
|
71
|
+
* @param {Object} [props={}] - Event properties and user data
|
|
72
|
+
* @param {Object} [props.context] - Framework-specific request context (required for auto-filled properties)
|
|
73
|
+
* @param {Object} [options={}] - Additional options
|
|
74
|
+
* @return {Promise<Object>} Event payload ready to send to CAPI
|
|
75
|
+
*/
|
|
29
76
|
async createEvent(eventName, props = {}, options = {}) {
|
|
30
77
|
const { context, ...p } = props
|
|
31
78
|
const { p: eventParams, o: r1 } = splitObject(p, SERVER_EVENT_PARAMS)
|
|
@@ -44,8 +91,11 @@ export default function FB({ pixelID, testEventCode, accessToken, framework }) {
|
|
|
44
91
|
)
|
|
45
92
|
}
|
|
46
93
|
|
|
47
|
-
|
|
48
|
-
|
|
94
|
+
const supportedFrameworks = ['astro', 'express', 'fastify', 'koa']
|
|
95
|
+
if (!supportedFrameworks.includes(framework)) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`Unsupported framework: ${framework}. Supported frameworks: ${supportedFrameworks.join(', ')}`,
|
|
98
|
+
)
|
|
49
99
|
}
|
|
50
100
|
|
|
51
101
|
const normalizedUserData = await normalizeUserData({
|
|
@@ -72,12 +122,32 @@ export default function FB({ pixelID, testEventCode, accessToken, framework }) {
|
|
|
72
122
|
return body
|
|
73
123
|
},
|
|
74
124
|
|
|
75
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Tracks an event and sends it to Facebook CAPI
|
|
127
|
+
* Combines createEvent and sendCAPIRequest
|
|
128
|
+
* @param {string} eventName - Name of the event to track
|
|
129
|
+
* @param {Object} [props={}] - Event properties and user data
|
|
130
|
+
* @param {Object} [props.context] - Framework-specific request context
|
|
131
|
+
* @param {Object} [options={}] - Additional options
|
|
132
|
+
* @param {boolean} [options.debug] - Enable debug logging
|
|
133
|
+
* @param {boolean} [options.dryRun] - Skip actual API call
|
|
134
|
+
* @param {string} [options.pixel_id] - Override default pixel ID
|
|
135
|
+
* @return {Promise<Object>} CAPI response
|
|
136
|
+
*/
|
|
76
137
|
async track(eventName, props = {}, options = {}) {
|
|
77
138
|
const body = await this.createEvent(eventName, props, options)
|
|
78
139
|
return this.sendCAPIRequest(body, options)
|
|
79
140
|
},
|
|
80
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Sends a raw request to Facebook Conversions API
|
|
144
|
+
* @param {Object} body - Request body containing event data
|
|
145
|
+
* @param {Object} [options={}] - Request options
|
|
146
|
+
* @param {boolean} [options.debug] - Log request body
|
|
147
|
+
* @param {boolean} [options.dryRun] - Skip actual API call
|
|
148
|
+
* @param {string} [options.pixel_id] - Override default pixel ID
|
|
149
|
+
* @return {Promise<Object>} CAPI response
|
|
150
|
+
*/
|
|
81
151
|
async sendCAPIRequest(body, options) {
|
|
82
152
|
const { debug = false, dryRun = false, pixel_id } = options
|
|
83
153
|
const pixel = pixel_id || pixelID
|
|
@@ -97,6 +167,20 @@ export default function FB({ pixelID, testEventCode, accessToken, framework }) {
|
|
|
97
167
|
}
|
|
98
168
|
}
|
|
99
169
|
|
|
170
|
+
// ========================
|
|
171
|
+
// Helper Functions
|
|
172
|
+
// ========================
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Gets default event parameters based on the framework and context
|
|
176
|
+
* @param {string} framework - Framework name (astro, express, fastify, koa)
|
|
177
|
+
* @param {Object} context - Framework-specific request context
|
|
178
|
+
* @return {Object} Default event parameters
|
|
179
|
+
* @property {string} event_id - Randomly generated event ID
|
|
180
|
+
* @property {string} action_source - Always set to 'website'
|
|
181
|
+
* @property {number} event_time - Unix timestamp in seconds
|
|
182
|
+
* @property {string} event_source_url - Full URL of the request
|
|
183
|
+
*/
|
|
100
184
|
function getEventParamDefaults(framework, context) {
|
|
101
185
|
return {
|
|
102
186
|
event_id: randomString(10),
|
|
@@ -106,13 +190,78 @@ function getEventParamDefaults(framework, context) {
|
|
|
106
190
|
}
|
|
107
191
|
}
|
|
108
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Extracts the full URL from framework-specific context
|
|
195
|
+
* @param {string} framework - Framework name (astro, express, fastify, koa)
|
|
196
|
+
* @param {Object} context - Framework-specific request context
|
|
197
|
+
* @return {string} Full URL including protocol, host, and path
|
|
198
|
+
*/
|
|
109
199
|
function getUrl(framework, context) {
|
|
110
200
|
if (framework === 'astro') return context.url.href
|
|
201
|
+
if (framework === 'express') {
|
|
202
|
+
return context.protocol + '://' + context.get('host') + context.originalUrl
|
|
203
|
+
}
|
|
204
|
+
if (framework === 'fastify') {
|
|
205
|
+
return context.protocol + '://' + context.hostname + context.url
|
|
206
|
+
}
|
|
207
|
+
if (framework === 'koa') {
|
|
208
|
+
return context.protocol + '://' + context.host + context.url
|
|
209
|
+
}
|
|
111
210
|
}
|
|
112
211
|
|
|
113
|
-
|
|
114
|
-
|
|
212
|
+
/**
|
|
213
|
+
* Extracts client IP address from framework-specific context
|
|
214
|
+
* @param {string} framework - Framework name (astro, express, fastify, koa)
|
|
215
|
+
* @param {Object} context - Framework-specific request context
|
|
216
|
+
* @return {string} Client IP address
|
|
217
|
+
*/
|
|
218
|
+
function getIP(framework, context) {
|
|
219
|
+
if (framework === 'astro') return context.clientAddress
|
|
220
|
+
if (framework === 'express')
|
|
221
|
+
return context.ip || context.connection?.remoteAddress
|
|
222
|
+
if (framework === 'fastify') return context.ip
|
|
223
|
+
if (framework === 'koa') return context.ip
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Extracts user agent string from framework-specific context
|
|
228
|
+
* @param {string} framework - Framework name (astro, express, fastify, koa)
|
|
229
|
+
* @param {Object} context - Framework-specific request context
|
|
230
|
+
* @return {string} User agent string
|
|
231
|
+
*/
|
|
232
|
+
function getUserAgent(framework, context) {
|
|
233
|
+
if (framework === 'astro') return context.request.headers.get('user-agent')
|
|
234
|
+
if (framework === 'express') return context.get('user-agent')
|
|
235
|
+
if (framework === 'fastify') return context.headers['user-agent']
|
|
236
|
+
if (framework === 'koa') return context.get('user-agent')
|
|
237
|
+
}
|
|
115
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Extracts cookie value from framework-specific context
|
|
241
|
+
* @param {string} framework - Framework name (astro, express, fastify, koa)
|
|
242
|
+
* @param {Object} context - Framework-specific request context
|
|
243
|
+
* @param {string} name - Cookie name
|
|
244
|
+
* @return {string} Cookie value
|
|
245
|
+
*/
|
|
246
|
+
function getCookie(framework, context, name) {
|
|
247
|
+
if (framework === 'astro') return context.cookies.get(name)?.value
|
|
248
|
+
if (framework === 'express') return context.cookies?.[name]
|
|
249
|
+
if (framework === 'fastify') return context.cookies?.[name]
|
|
250
|
+
if (framework === 'koa') return context.cookies?.get(name)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Normalizes and enriches user data for Facebook CAPI
|
|
255
|
+
* Hashes required properties and adds client information from request context
|
|
256
|
+
* @param {Object} params - Parameters object
|
|
257
|
+
* @param {Object} params.context - Framework-specific request context
|
|
258
|
+
* @param {string} params.framework - Framework name (astro, express, fastify, koa)
|
|
259
|
+
* @param {Object} params.userProps - User properties to normalize
|
|
260
|
+
* @param {string} [params.fbc] - Facebook click ID cookie
|
|
261
|
+
* @param {string} [params.fbp] - Facebook browser ID cookie
|
|
262
|
+
* @return {Promise<Object>} Normalized user data with hashed properties and client info
|
|
263
|
+
*/
|
|
264
|
+
async function normalizeUserData({ context, framework, userProps, fbc, fbp }) {
|
|
116
265
|
const { p: userPropsToHash, o: otherUserProps } = splitObject(
|
|
117
266
|
userProps,
|
|
118
267
|
USER_PROPERTIES_TO_HASH,
|
|
@@ -120,13 +269,11 @@ async function normalizeUserData({ context, framework, userProps, fbc, fbp }) {
|
|
|
120
269
|
|
|
121
270
|
const hashedProps = await formatUserData(userPropsToHash, sha256Hash)
|
|
122
271
|
|
|
123
|
-
// Get
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (!fbc) fbc = context.cookies.get('_fbc')?.value
|
|
129
|
-
}
|
|
272
|
+
// Get client data from context
|
|
273
|
+
const ip = getIP(framework, context)
|
|
274
|
+
const userAgent = getUserAgent(framework, context)
|
|
275
|
+
if (!fbp) fbp = getCookie(framework, context, '_fbp')
|
|
276
|
+
if (!fbc) fbc = getCookie(framework, context, '_fbc')
|
|
130
277
|
|
|
131
278
|
const userData = omitEmpty({
|
|
132
279
|
...hashedProps,
|
package/lib/fb/readme.md
CHANGED
|
@@ -1,42 +1,43 @@
|
|
|
1
|
-
|
|
1
|
+
Designed to provide a unified interface for Facebook Pixel and Conversion API.
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
Config
|
|
2
6
|
|
|
3
7
|
```js
|
|
4
8
|
export default {
|
|
5
9
|
fb: {
|
|
6
10
|
pixelID: 'PIXEL ID',
|
|
7
11
|
testEventCode: 'TEST EVENT CODE',
|
|
12
|
+
capiEndpoint: '/api/tracking/facebook/',
|
|
13
|
+
accessToken: import.meta.env.FB_CAPI_TOKEN,
|
|
14
|
+
framework: 'astro', // or 'express', 'fastify', 'koa'
|
|
8
15
|
},
|
|
9
16
|
}
|
|
10
17
|
```
|
|
11
18
|
|
|
12
|
-
|
|
19
|
+
Browser
|
|
13
20
|
|
|
14
21
|
```js
|
|
15
22
|
import FB from '@splendidlabz/tracking/fb/web'
|
|
16
|
-
import config from './
|
|
17
|
-
|
|
18
|
-
export const fb = FB({
|
|
19
|
-
...config.fb,
|
|
20
|
-
capiEndpoint: '/api/tracking/facebook/',
|
|
21
|
-
})
|
|
23
|
+
import config from './config'
|
|
24
|
+
export const fb = FB(config.fb)
|
|
22
25
|
```
|
|
23
26
|
|
|
24
27
|
Server
|
|
25
28
|
|
|
26
29
|
```js
|
|
27
30
|
import FB from '@splendidlabz/tracking/fb/node'
|
|
28
|
-
import config from './
|
|
29
|
-
|
|
30
|
-
export const fb = FB({
|
|
31
|
-
...config.fb,
|
|
32
|
-
framework: 'astro',
|
|
33
|
-
accessToken: import.meta.env.FB_CAPI_TOKEN,
|
|
34
|
-
})
|
|
31
|
+
import config from './config'
|
|
32
|
+
export const fb = FB(config.fb)
|
|
35
33
|
```
|
|
36
34
|
|
|
37
|
-
|
|
35
|
+
Include API to dedupe events through CAPI
|
|
36
|
+
|
|
37
|
+
**Astro:**
|
|
38
38
|
|
|
39
39
|
```js
|
|
40
|
+
// /api/tracking/facebook/
|
|
40
41
|
import { fb } from '@/services/tracking/node'
|
|
41
42
|
import { JSONResponse, parseData } from '@splendidlabz/astro/server'
|
|
42
43
|
export const prerender = false
|
|
@@ -49,6 +50,143 @@ export async function POST(context) {
|
|
|
49
50
|
}
|
|
50
51
|
```
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
**Express:**
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
// Requires cookie-parser middleware for cookie support
|
|
57
|
+
import express from 'express'
|
|
58
|
+
import cookieParser from 'cookie-parser'
|
|
59
|
+
import { fb } from './services/tracking/node.js'
|
|
60
|
+
|
|
61
|
+
const app = express()
|
|
62
|
+
app.use(express.json())
|
|
63
|
+
app.use(cookieParser())
|
|
64
|
+
|
|
65
|
+
app.post('/api/tracking/facebook', async (req, res) => {
|
|
66
|
+
const { response, error } = await fb.sendEvent(req.body)
|
|
67
|
+
if (response) return res.status(200).json(response.body)
|
|
68
|
+
if (error) return res.status(500).json(error)
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Fastify:**
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
// Requires @fastify/cookie plugin for cookie support
|
|
76
|
+
import Fastify from 'fastify'
|
|
77
|
+
import fastifyCookie from '@fastify/cookie'
|
|
78
|
+
import { fb } from './services/tracking/node.js'
|
|
79
|
+
|
|
80
|
+
const fastify = Fastify()
|
|
81
|
+
await fastify.register(fastifyCookie)
|
|
82
|
+
|
|
83
|
+
fastify.post('/api/tracking/facebook', async (request, reply) => {
|
|
84
|
+
const { response, error } = await fb.sendEvent(request.body)
|
|
85
|
+
if (response) return reply.status(200).send(response.body)
|
|
86
|
+
if (error) return reply.status(500).send(error)
|
|
87
|
+
})
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Koa:**
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
// Requires koa-bodyparser and koa-cookie middleware
|
|
94
|
+
import Koa from 'koa'
|
|
95
|
+
import bodyParser from 'koa-bodyparser'
|
|
96
|
+
import cookie from 'koa-cookie'
|
|
97
|
+
import { fb } from './services/tracking/node.js'
|
|
98
|
+
|
|
99
|
+
const app = new Koa()
|
|
100
|
+
app.use(bodyParser())
|
|
101
|
+
app.use(cookie())
|
|
102
|
+
|
|
103
|
+
app.use(async (ctx) => {
|
|
104
|
+
if (ctx.path === '/api/tracking/facebook' && ctx.method === 'POST') {
|
|
105
|
+
const { response, error } = await fb.sendEvent(ctx.request.body)
|
|
106
|
+
if (response) {
|
|
107
|
+
ctx.status = 200
|
|
108
|
+
ctx.body = response.body
|
|
109
|
+
}
|
|
110
|
+
if (error) {
|
|
111
|
+
ctx.status = 500
|
|
112
|
+
ctx.body = error
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Usage
|
|
119
|
+
|
|
120
|
+
Browser:
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
fb.track('event_name', { ...props })
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Node (requires passing the request context):
|
|
127
|
+
|
|
128
|
+
**Astro:**
|
|
129
|
+
|
|
130
|
+
```js
|
|
131
|
+
fb.track('event_name', { context, ...props })
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Express:**
|
|
135
|
+
|
|
136
|
+
```js
|
|
137
|
+
app.post('/some-route', async (req, res) => {
|
|
138
|
+
await fb.track('event_name', { context: req, ...props })
|
|
139
|
+
res.json({ success: true })
|
|
140
|
+
})
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Fastify:**
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
fastify.post('/some-route', async (request, reply) => {
|
|
147
|
+
await fb.track('event_name', { context: request, ...props })
|
|
148
|
+
reply.send({ success: true })
|
|
149
|
+
})
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Koa:**
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
app.use(async (ctx) => {
|
|
156
|
+
if (ctx.path === '/some-route' && ctx.method === 'POST') {
|
|
157
|
+
await fb.track('event_name', { context: ctx, ...props })
|
|
158
|
+
ctx.body = { success: true }
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Event Reference
|
|
164
|
+
|
|
165
|
+
- [Standard Event Reference](https://developers.facebook.com/docs/meta-pixel/reference#standard-events)
|
|
166
|
+
- [Advanced Matching Reference](https://developers.facebook.com/docs/meta-pixel/advanced/advanced-matching/)
|
|
167
|
+
- [Conversion API Params Reference](https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters)
|
|
168
|
+
|
|
169
|
+
## Testing Your Implementation
|
|
170
|
+
|
|
171
|
+
Go to `Events Manager > Datasets > Test Events` and copy your event tracking code. Add it to your config.
|
|
172
|
+
|
|
173
|
+
**IMPORTANT**: Remove `testEventCode` after your tests! You don't want this in your production code.
|
|
174
|
+
|
|
175
|
+
```js
|
|
176
|
+
export default {
|
|
177
|
+
fb: {
|
|
178
|
+
pixelID: 'PIXEL ID',
|
|
179
|
+
testEventCode: 'TEST EVENT CODE',
|
|
180
|
+
// ...
|
|
181
|
+
},
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Under the same `Test Events` tab, enter a website to test with.
|
|
186
|
+
|
|
187
|
+
- Note that localhost cannot be used for browser-testing — FB requires a valid domain
|
|
188
|
+
- To test if server & browser events are deduplicated, use a local tunnel like ngrok.
|
|
189
|
+
- Make sure your website — and your tunnel — is listed under your Pixel's allow list under
|
|
190
|
+
`Dataset > Settings > Allow list`. Feel free to remove the tunnel from the allow list after testing.
|
|
191
|
+
|
|
192
|
+
- Context is to automatically get Client IP Address and User Agent.
|
package/lib/fb/web.js
CHANGED
|
@@ -1,18 +1,46 @@
|
|
|
1
1
|
import { notEmpty, splitObject } from '@splendidlabz/utils'
|
|
2
2
|
import { getCookie, randomString, sha256Hash } from '@splendidlabz/utils/dom'
|
|
3
|
+
import zlFetch from 'zl-fetch'
|
|
3
4
|
import {
|
|
4
5
|
STANDARD_EVENTS,
|
|
5
6
|
USER_PROPERTIES,
|
|
6
7
|
USER_PROPERTIES_TO_HASH,
|
|
7
8
|
} from './consts.js'
|
|
8
|
-
|
|
9
|
-
import zlFetch from 'zl-fetch'
|
|
10
9
|
import { formatUserData } from './utils.js'
|
|
11
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Creates a Facebook Pixel and CAPI tracker for client-side tracking
|
|
13
|
+
* @param {Object} config - Configuration options
|
|
14
|
+
* @param {string} config.pixelID - Facebook Pixel ID
|
|
15
|
+
* @param {string} [config.testEventCode] - Test event code for debugging
|
|
16
|
+
* @param {string} config.capiEndpoint - API endpoint for sending CAPI requests
|
|
17
|
+
* @return {Object} FB tracking instance
|
|
18
|
+
* @property {Function} init - Initialize Facebook Pixel
|
|
19
|
+
* @property {Function} createEvent - Create event data for both Pixel and CAPI
|
|
20
|
+
* @property {Function} track - Track event via both Pixel and CAPI
|
|
21
|
+
* @property {Function} sendPixelEvent - Send event to Facebook Pixel
|
|
22
|
+
* @property {Function} sendCAPIRequest - Send event to CAPI endpoint
|
|
23
|
+
*/
|
|
12
24
|
export default function FB({ pixelID, testEventCode, capiEndpoint }) {
|
|
13
25
|
return {
|
|
26
|
+
/**
|
|
27
|
+
* Initializes the Facebook Pixel on the page
|
|
28
|
+
* @return {void}
|
|
29
|
+
*/
|
|
14
30
|
init: _ => initPixel(pixelID),
|
|
15
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Creates event data for both Facebook Pixel and CAPI
|
|
34
|
+
* @param {string} event_name - Name of the event to track
|
|
35
|
+
* @param {Object} [props={}] - Event properties and user data
|
|
36
|
+
* @param {string} [props.eventURL] - Custom URL for the event (defaults to current page)
|
|
37
|
+
* @param {string} [props.actionSource] - Action source (defaults to 'website')
|
|
38
|
+
* @param {Object} [options={}] - Additional options
|
|
39
|
+
* @param {boolean} [options.debug] - Enable debug logging
|
|
40
|
+
* @return {Promise<Object>} Object containing pixelData and capiData
|
|
41
|
+
* @property {Object} pixelData - Data formatted for Facebook Pixel
|
|
42
|
+
* @property {Object} capiData - Data formatted for CAPI
|
|
43
|
+
*/
|
|
16
44
|
async createEvent(event_name, props = {}, options = {}) {
|
|
17
45
|
const { debug = false } = options
|
|
18
46
|
const { eventURL, actionSource, ...r } = props
|
|
@@ -61,6 +89,15 @@ export default function FB({ pixelID, testEventCode, capiEndpoint }) {
|
|
|
61
89
|
return { pixelData, capiData }
|
|
62
90
|
},
|
|
63
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Tracks an event by sending to both Facebook Pixel and CAPI
|
|
94
|
+
* @param {string} event_name - Name of the event to track
|
|
95
|
+
* @param {Object} [props={}] - Event properties and user data
|
|
96
|
+
* @param {Object} [options={}] - Additional options
|
|
97
|
+
* @param {boolean} [options.debug] - Enable debug logging
|
|
98
|
+
* @param {boolean} [options.dryRun] - Skip actual tracking (for testing)
|
|
99
|
+
* @return {Promise<Object>} CAPI response body
|
|
100
|
+
*/
|
|
64
101
|
async track(event_name, props = {}, options = {}) {
|
|
65
102
|
const { debug = false, dryRun = false } = options
|
|
66
103
|
const { pixelData, capiData } = await this.createEvent(
|
|
@@ -78,11 +115,20 @@ export default function FB({ pixelID, testEventCode, capiEndpoint }) {
|
|
|
78
115
|
return response.body
|
|
79
116
|
},
|
|
80
117
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
118
|
+
/**
|
|
119
|
+
* Sends an event to Facebook Pixel
|
|
120
|
+
* Supports standard events, custom events, and advanced matching
|
|
121
|
+
* @param {Object} data - Event data
|
|
122
|
+
* @param {string} data.pixelID - Pixel ID
|
|
123
|
+
* @param {string} data.eventID - Event ID for deduplication
|
|
124
|
+
* @param {string} data.event_name - Event name (use 'init' to initialize pixel)
|
|
125
|
+
* @param {Object} [data.user_data] - User data for advanced matching
|
|
126
|
+
* @param {Object} [options] - Options
|
|
127
|
+
* @param {boolean} [options.debug] - Enable debug logging
|
|
128
|
+
* @return {void}
|
|
129
|
+
* @see https://developers.facebook.com/docs/meta-pixel/reference#standard-events
|
|
130
|
+
* @see https://developers.facebook.com/docs/meta-pixel/advanced/advanced-matching/
|
|
131
|
+
*/
|
|
86
132
|
sendPixelEvent(data, { debug = false }) {
|
|
87
133
|
const { pixelID, eventID, event_name, user_data = {}, ...rest } = data
|
|
88
134
|
const hasUserData = notEmpty(user_data)
|
|
@@ -109,13 +155,24 @@ export default function FB({ pixelID, testEventCode, capiEndpoint }) {
|
|
|
109
155
|
}
|
|
110
156
|
},
|
|
111
157
|
|
|
112
|
-
|
|
158
|
+
/**
|
|
159
|
+
* Sends a request to the CAPI endpoint
|
|
160
|
+
* @param {Object} data - CAPI event data
|
|
161
|
+
* @return {Promise<Object>} Response from CAPI endpoint
|
|
162
|
+
* @see https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters
|
|
163
|
+
*/
|
|
113
164
|
sendCAPIRequest(data) {
|
|
114
165
|
return zlFetch.post(capiEndpoint, { body: data })
|
|
115
166
|
},
|
|
116
167
|
}
|
|
117
168
|
}
|
|
118
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Initializes the Facebook Pixel by loading the fbevents.js script
|
|
172
|
+
* @param {string} pixelID - Facebook Pixel ID
|
|
173
|
+
* @return {void}
|
|
174
|
+
* @throws {Error} If called outside of browser environment
|
|
175
|
+
*/
|
|
119
176
|
function initPixel(pixelID) {
|
|
120
177
|
// eslint-disable-next-line
|
|
121
178
|
!(function (f, b, e, v, n, t, s) {
|
package/lib/posthog/node.js
CHANGED
|
@@ -72,7 +72,7 @@ export default function (config = {}) {
|
|
|
72
72
|
* @param {string} [properties.userId] - User ID (used for distinct ID)
|
|
73
73
|
* @param {Object} [properties.context] - Request context (for cookie access)
|
|
74
74
|
* @param {...*} properties.rest - Additional event properties
|
|
75
|
-
* @return {Promise|undefined} PostHog capture promise, or undefined if
|
|
75
|
+
* @return {Promise|undefined} PostHog capture promise, or undefined if trackEvents is false
|
|
76
76
|
*/
|
|
77
77
|
capture(event, properties = {}) {
|
|
78
78
|
if (!trackEvents) return
|
|
@@ -88,7 +88,7 @@ export default function (config = {}) {
|
|
|
88
88
|
* Sets person properties on the current user
|
|
89
89
|
* @param {Object} [set={}] - Properties to set (will overwrite existing values)
|
|
90
90
|
* @param {Object} [once={}] - Properties to set only once (won't overwrite existing values)
|
|
91
|
-
* @return {Promise|undefined} PostHog capture promise, or undefined if
|
|
91
|
+
* @return {Promise|undefined} PostHog capture promise, or undefined if trackEvents is false
|
|
92
92
|
*/
|
|
93
93
|
setPersonProperties(set = {}, once = {}) {
|
|
94
94
|
if (!trackEvents) return
|
|
@@ -104,7 +104,7 @@ export default function (config = {}) {
|
|
|
104
104
|
/**
|
|
105
105
|
* Removes person properties from the current user
|
|
106
106
|
* @param {string[]} properties - List of property names to remove
|
|
107
|
-
* @return {Promise|undefined} PostHog capture promise, or undefined if
|
|
107
|
+
* @return {Promise|undefined} PostHog capture promise, or undefined if trackEvents is false
|
|
108
108
|
*/
|
|
109
109
|
removePersonProperties(properties) {
|
|
110
110
|
if (!trackEvents) return
|
package/lib/posthog/readme.md
CHANGED
|
@@ -15,3 +15,11 @@ This package assumes you're using Posthog for both web and node.js.
|
|
|
15
15
|
|
|
16
16
|
- Get distinct_id from cookies on the backend automatically when using Astro — just provide context.
|
|
17
17
|
- Use `trackEvents:false` to disable tracking completely when developing.
|
|
18
|
+
|
|
19
|
+
## Filtering out local development environments
|
|
20
|
+
|
|
21
|
+
You can create filters which include more than one value by separating values with a comma. We use such a filter to exclude events from local development environments, for example:
|
|
22
|
+
|
|
23
|
+
Host ≠ (doesn’t equal) localhost:8000,localhost:5000,127.0.0.1:8000
|
|
24
|
+
|
|
25
|
+
You can also create filters based on pre-prepared cohorts of users, which is especially useful if you’re using cohorts with Feature Flags or to run Experiments. To do this, simply select the cohort you wish to add to your internal and test user list.
|
package/lib/posthog/web.js
CHANGED
|
@@ -41,7 +41,8 @@ export default function (config = {}) {
|
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* Set properties for the current person
|
|
44
|
-
* @param {
|
|
44
|
+
* @param {object} [set={}] - Properties to set (will overwrite existing values)
|
|
45
|
+
* @param {object} [once={}] - Properties to set only once (won't overwrite existing values)
|
|
45
46
|
* @returns {void}
|
|
46
47
|
*/
|
|
47
48
|
setPersonProperties(set = {}, once = {}) {
|
|
@@ -50,8 +51,9 @@ export default function (config = {}) {
|
|
|
50
51
|
},
|
|
51
52
|
|
|
52
53
|
/**
|
|
54
|
+
* Identify a user with PostHog. If the distinctId differs from the existing cookie ID, creates a new identity; otherwise updates properties.
|
|
53
55
|
* @param {string} distinctId - New unique identifier for the user (email, userId, etc.)
|
|
54
|
-
* @param {object} properties - The properties to set for the user
|
|
56
|
+
* @param {object} [properties={}] - The properties to set for the user
|
|
55
57
|
* @returns {void}
|
|
56
58
|
*/
|
|
57
59
|
identify(distinctId, properties = {}) {
|
|
@@ -67,6 +69,7 @@ export default function (config = {}) {
|
|
|
67
69
|
},
|
|
68
70
|
|
|
69
71
|
/**
|
|
72
|
+
* Create an alias for a user, linking two distinct IDs together
|
|
70
73
|
* @param {string} distinctId - New unique identifier for the user
|
|
71
74
|
* @param {string} aliasId - Alias for the user
|
|
72
75
|
* @returns {void}
|