@segment/analytics-browser-actions-facebook-conversions-api-web 1.8.1-staging-701e5e031.0 → 1.9.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.
@@ -2,184 +2,212 @@ import { FBEvent, UserData, EventOptions, FBClient, FBStandardEventType, FBNonSt
2
2
  import { Payload } from './generated-types'
3
3
  import { Settings } from '../generated-types'
4
4
  import { UniversalStorage, Analytics } from '@segment/analytics-next'
5
- import { US_STATE_CODES, COUNTRY_CODES, MAX_INIT_COUNT, INIT_COUNT_KEY, USER_DATA_KEY } from '../constants'
5
+ import {US_STATE_CODES, COUNTRY_CODES, MAX_INIT_COUNT, INIT_COUNT_KEY, USER_DATA_KEY } from '../constants'
6
6
  import { storageFallback, setStorageInitCount } from '../functions'
7
7
  import { getNotVisibleForEvent } from './depends-on'
8
8
 
9
9
  export function send(client: FBClient, payload: Payload, settings: Settings, analytics: Analytics) {
10
- const { pixelId } = settings
11
- const { event_config: { custom_event_name, event_name } = {} } = payload
10
+ const { pixelId } = settings
11
+ const {
12
+ event_config: {
13
+ custom_event_name,
14
+ event_name
15
+ } = {}
16
+ } = payload
12
17
 
13
- const isCustom = event_name === 'CustomEvent' ? true : false
18
+ const isCustom = event_name === 'CustomEvent' ? true : false
14
19
 
15
- const errorMessage = validate(payload)
20
+ const errorMessage = validate(payload)
16
21
 
17
- if (errorMessage) {
18
- console.warn(`${errorMessage}`)
19
- return
20
- }
21
-
22
- const fbEvent = formatFBEvent(payload)
23
-
24
- maybeSendUserData(client, payload, settings, analytics)
25
-
26
- const options = formatOptions(payload)
27
-
28
- if (isCustom) {
29
- client('trackSingleCustom', pixelId, custom_event_name as string, { ...fbEvent }, options)
30
- } else {
31
- client('trackSingle', pixelId, event_name as FBStandardEventType, { ...fbEvent }, options)
32
- }
22
+ if(errorMessage) {
23
+ console.warn(`${errorMessage}`)
24
+ return
25
+ }
26
+
27
+ const fbEvent = formatFBEvent(payload)
28
+
29
+ maybeSendUserData(client, payload, settings, analytics)
30
+
31
+ const options = formatOptions(payload)
32
+
33
+ if(isCustom){
34
+ client(
35
+ 'trackSingleCustom',
36
+ pixelId,
37
+ custom_event_name as string,
38
+ { ...fbEvent },
39
+ options
40
+ )
41
+ }
42
+ else {
43
+ client(
44
+ 'trackSingle',
45
+ pixelId,
46
+ event_name as FBStandardEventType,
47
+ { ...fbEvent },
48
+ options
49
+ )
50
+ }
33
51
  }
34
52
 
35
53
  function validate(payload: Payload): string | undefined {
36
- const {
37
- event_config: { event_name },
38
- content_ids,
39
- contents
40
- } = payload
41
-
42
- if (['AddToCart', 'Purchase', 'ViewContent'].includes(event_name)) {
43
- if (
44
- (!content_ids || (Array.isArray(content_ids) && content_ids.length === 0)) &&
45
- (!contents || (Array.isArray(contents) && contents.length === 0))
46
- ) {
47
- return `At least one of content_ids or contents is required for the ${event_name} event.`
54
+ const {
55
+ event_config: { event_name },
56
+ content_ids,
57
+ contents
58
+ } = payload
59
+
60
+ if(['AddToCart', 'Purchase', 'ViewContent'].includes(event_name)){
61
+ if ((!content_ids || (Array.isArray(content_ids) && content_ids.length === 0)) && (!contents || (Array.isArray(contents) && contents.length === 0))) {
62
+ return `At least one of content_ids or contents is required for the ${event_name} event.`
63
+ }
48
64
  }
49
- }
50
65
 
51
- return undefined
66
+ return undefined
52
67
  }
53
68
 
54
69
  function formatFBEvent(payload: Payload): FBEvent {
55
- const {
56
- content_category,
57
- content_ids,
58
- content_name,
59
- content_type,
60
- contents,
61
- currency,
62
- delivery_category,
63
- num_items,
64
- value,
65
- predicted_ltv,
66
- net_revenue,
67
- custom_data,
68
- event_config: { event_name, show_fields } = {}
69
- } = payload
70
-
71
- const fbEvent: FBEvent = {
72
- ...(content_category ? { content_category } : {}),
73
- ...(content_ids && Array.isArray(content_ids) && content_ids.length > 0 ? { content_ids } : {}),
74
- ...(content_name ? { content_name } : {}),
75
- ...(content_type ? { content_type } : {}),
76
- ...(contents && Array.isArray(contents) && contents.length > 0 ? { contents } : {}),
77
- ...(currency ? { currency } : {}),
78
- ...(delivery_category ? { delivery_category } : {}),
79
- ...(typeof num_items === 'number' ? { num_items } : {}),
80
- ...(typeof value === 'number' ? { value } : {}),
81
- ...(typeof predicted_ltv === 'number' ? { predicted_ltv } : {}),
82
- ...(typeof net_revenue === 'number' ? { net_revenue } : {}),
83
- ...(custom_data && Object.entries(custom_data).length > 0 ? { custom_data } : {})
84
- }
85
-
86
- if (show_fields === false) {
87
- // If show_fields is false we delete values for fields which are hidden in the UI.
88
- const fieldsToDelete = getNotVisibleForEvent(event_name as FBStandardEventType | FBNonStandardEventType)
89
- fieldsToDelete.forEach((field) => {
90
- if (field in fbEvent) {
91
- delete fbEvent[field as keyof typeof fbEvent]
92
- }
93
- })
94
- }
95
-
96
- return Object.keys(fbEvent).length > 0 ? fbEvent : {}
70
+ const {
71
+ content_category,
72
+ content_ids,
73
+ content_name,
74
+ content_type,
75
+ contents,
76
+ currency,
77
+ delivery_category,
78
+ num_items,
79
+ value,
80
+ predicted_ltv,
81
+ net_revenue,
82
+ custom_data,
83
+ event_config: {
84
+ event_name,
85
+ show_fields
86
+ } = {}
87
+ } = payload
88
+
89
+ const fbEvent: FBEvent = {
90
+ ...(content_category ? { content_category } : {}),
91
+ ...(content_ids && Array.isArray(content_ids) && content_ids.length > 0 ? { content_ids } : {}),
92
+ ...(content_name ? { content_name } : {}),
93
+ ...(content_type ? { content_type } : {}),
94
+ ...(contents && Array.isArray(contents) && contents.length > 0 ? { contents } : {}),
95
+ ...(currency ? { currency } : {}),
96
+ ...(delivery_category ? { delivery_category } : {}),
97
+ ...(typeof num_items === 'number' ? { num_items } : {}),
98
+ ...(typeof value === 'number' ? { value } : {}),
99
+ ...(typeof predicted_ltv === 'number' ? { predicted_ltv } : {}),
100
+ ...(typeof net_revenue === 'number' ? { net_revenue } : {}),
101
+ ...(custom_data && Object.entries(custom_data).length > 0 ? { custom_data } : {})
102
+ }
103
+
104
+ if(show_fields === false){
105
+ // If show_fields is false we delete values for fields which are hidden in the UI.
106
+ const fieldsToDelete = getNotVisibleForEvent(event_name as FBStandardEventType | FBNonStandardEventType)
107
+ fieldsToDelete.forEach(field => {
108
+ if (field in fbEvent) {
109
+ delete fbEvent[field as keyof typeof fbEvent]
110
+ }
111
+ })
112
+ }
113
+
114
+ return Object.keys(fbEvent).length > 0 ? fbEvent : {}
97
115
  }
98
116
 
99
117
  function formatOptions(payload: Payload): EventOptions | undefined {
100
- const { eventID, eventSourceUrl } = payload
101
- const options: EventOptions = {
102
- ...(eventID ? { eventID } : {}),
103
- ...(eventSourceUrl ? { eventSourceUrl } : {})
104
- }
105
- return Object.values(options).some(Boolean) ? options : undefined
118
+ const { eventID, eventSourceUrl } = payload
119
+ const options: EventOptions = {
120
+ ...(eventID ? { eventID } : {}),
121
+ ...(eventSourceUrl ? { eventSourceUrl } : {}),
122
+ }
123
+ return Object.values(options).some(Boolean) ? options : undefined
106
124
  }
107
125
 
108
126
  function maybeSendUserData(client: FBClient, payload: Payload, settings: Settings, analytics: Analytics) {
109
- const { pixelId } = settings
110
- const { userData } = payload
111
- const userDataFormatted = formatUserData(userData)
112
-
113
- if (userDataFormatted) {
114
- const storage = (analytics.storage as UniversalStorage<Record<string, string>>) ?? storageFallback
115
- const initCountFromStorage: string | null = storage.get(INIT_COUNT_KEY)
116
- const initCount: number | undefined =
117
- initCountFromStorage && !isNaN(Number(initCountFromStorage)) ? parseInt(initCountFromStorage, 10) : undefined
118
-
119
- if (typeof initCount === 'number' && initCount < MAX_INIT_COUNT) {
120
- client('init', pixelId, userDataFormatted)
121
- setStorageInitCount(analytics, initCount + 1)
127
+ const { pixelId } = settings
128
+ const { userData } = payload
129
+ const userDataFormatted = formatUserData(userData)
130
+
131
+ if(userDataFormatted) {
132
+ const storage = (analytics.storage as UniversalStorage<Record<string, string>>) ?? storageFallback
133
+ const initCountFromStorage: string | null = storage.get(INIT_COUNT_KEY)
134
+ const initCount: number | undefined = (initCountFromStorage && !isNaN(Number(initCountFromStorage)))
135
+ ? parseInt(initCountFromStorage, 10)
136
+ : undefined
137
+
138
+ if(typeof initCount === 'number' && initCount < MAX_INIT_COUNT) {
139
+ client('init', pixelId, userDataFormatted)
140
+ setStorageInitCount(analytics, initCount + 1)
141
+ }
142
+
143
+ storage.set(USER_DATA_KEY, JSON.stringify(userDataFormatted))
122
144
  }
123
-
124
- storage.set(USER_DATA_KEY, JSON.stringify(userDataFormatted))
125
- }
126
- }
145
+ }
127
146
 
128
147
  function formatUserData(userData: Payload['userData']): UserData | undefined {
129
- if (!userData) {
130
- return undefined
131
- }
132
-
133
- const { external_id, em, ph, fn, ln, ge, db, ct, st, zp, country } = userData
134
-
135
- const dbFormatted = formatDate(db)
136
- const stFormatted = fromMap(US_STATE_CODES, st)
137
- const countryFormatted = fromMap(COUNTRY_CODES, country)
138
-
139
- const ud: UserData = {
140
- ...(typeof em === 'string' ? { em: em.toLowerCase().trim() } : {}), // lowercase and trim whitespace
141
- ...(typeof ph === 'string' ? { ph: ph.replace(/\D/g, '') } : {}), // remove non-numeric characters
142
- ...(typeof fn === 'string' ? { fn: fn.toLowerCase().trim() } : {}), // lowercase and trim whitespace
143
- ...(typeof ln === 'string' ? { ln: ln.toLowerCase().trim() } : {}), // lowercase and trim whitespace
144
- ...(typeof ge === 'string' ? { ge: ge.toLowerCase().trim() } : {}), // lowercase and trim whitespace
145
- ...(typeof dbFormatted === 'string' ? { db: dbFormatted } : {}), // format date to YYYYMMDD
146
- ...(typeof ct === 'string' ? { ct: ct.toLowerCase().replace(/\s+/g, '') } : {}), // lowercase and replace any whitespace
147
- ...(typeof stFormatted === 'string' ? { st: stFormatted } : {}), // lowercase 2 character state code
148
- ...(typeof zp === 'string' ? { zp: zp.trim() } : {}),
149
- ...(typeof countryFormatted === 'string' ? { country: countryFormatted } : {}), // lowercase 2 character country code
150
- ...(typeof external_id === 'string' ? { external_id: external_id.trim() } : {}) // trim whitespace
151
- }
152
-
153
- if (Object.keys(ud).length === 0) {
154
- return undefined
155
- }
156
- return ud
148
+ if(!userData){
149
+ return undefined
150
+ }
151
+
152
+ const {
153
+ external_id,
154
+ em,
155
+ ph,
156
+ fn,
157
+ ln,
158
+ ge,
159
+ db,
160
+ ct,
161
+ st,
162
+ zp,
163
+ country
164
+ } = userData
165
+
166
+ const dbFormatted = formatDate(db)
167
+ const stFormatted = fromMap(US_STATE_CODES, st)
168
+ const countryFormatted = fromMap(COUNTRY_CODES, country)
169
+
170
+ const ud: UserData = {
171
+ ...(typeof em === 'string' ? {em: em.toLowerCase().trim()} : {}), // lowercase and trim whitespace
172
+ ...(typeof ph === 'string' ? {ph: ph.replace(/\D/g, '')} : {}), // remove non-numeric characters
173
+ ...(typeof fn === 'string' ? {fn: fn.toLowerCase().trim()} : {}), // lowercase and trim whitespace
174
+ ...(typeof ln === 'string' ? {ln: ln.toLowerCase().trim()} : {}), // lowercase and trim whitespace
175
+ ...(typeof ge === 'string' && ['m', 'f'].includes(ge) ? { ge: ge as 'm' | 'f' } : {}),
176
+ ...(typeof dbFormatted === 'string' ? {db: dbFormatted} : {}), // format date to YYYYMMDD
177
+ ...(typeof ct === 'string' ? {ct: ct.toLowerCase().replace(/\s+/g, '')} : {}), // lowercase and replace any whitespace
178
+ ...(typeof stFormatted === 'string' ? {st: stFormatted} : {}), // lowercase 2 character state code
179
+ ...(typeof zp === 'string' ? {zp: zp.trim()} : {}),
180
+ ...(typeof countryFormatted === 'string' ? {country: countryFormatted} : {}), // lowercase 2 character country code
181
+ ...(typeof external_id === 'string' ? {external_id: external_id.trim()} : {}) // trim whitespace
182
+ }
183
+
184
+ if(Object.keys(ud).length === 0){
185
+ return undefined
186
+ }
187
+ return ud
157
188
  }
158
189
 
159
190
  function formatDate(isoDate?: string): string | undefined {
160
- if (!isoDate || typeof isoDate !== 'string') {
161
- return undefined
162
- }
163
- const date = new Date(isoDate)
164
- if (isNaN(date.getTime())) {
165
- return undefined
166
- }
167
- const year = date.getUTCFullYear()
168
- const month = (date.getUTCMonth() + 1).toString().padStart(2, '0')
169
- const day = date.getUTCDate().toString().padStart(2, '0')
170
- return `${year}${month}${day}`
191
+ if (!isoDate || typeof isoDate !== 'string') {
192
+ return undefined
193
+ }
194
+ const date = new Date(isoDate)
195
+ if (isNaN(date.getTime())) {
196
+ return undefined
197
+ }
198
+ const year = date.getUTCFullYear()
199
+ const month = (date.getUTCMonth() + 1).toString().padStart(2, '0')
200
+ const day = date.getUTCDate().toString().padStart(2, '0')
201
+ return `${year}${month}${day}`
171
202
  }
172
203
 
173
204
  function fromMap(map: Map<string, string>, value?: string): string | undefined {
174
- const cleaned = value
175
- ?.toLowerCase()
176
- .replace(/[^a-z]/g, '')
177
- .trim()
178
- if (!cleaned) {
179
- return undefined
180
- }
181
- if (cleaned.length === 2 && Array.from(map.values()).includes(cleaned)) {
182
- return cleaned
183
- }
184
- return map.get(cleaned) || undefined
185
- }
205
+ const cleaned = value?.toLowerCase().replace(/[^a-z]/g, '').trim()
206
+ if (!cleaned) {
207
+ return undefined
208
+ }
209
+ if(cleaned.length === 2 && Array.from(map.values()).includes(cleaned)) {
210
+ return cleaned
211
+ }
212
+ return map.get(cleaned) || undefined
213
+ }
@@ -91,7 +91,7 @@ export interface Payload {
91
91
  [k: string]: unknown
92
92
  }
93
93
  /**
94
- * This ID can be any unique string. Event ID is used to deduplicate events sent by both Facebook Pixel and Conversions API.
94
+ * This ID can be any unique string. Event ID is used to deduplicate events sent both the server side Conversions API and the browser Pixel.
95
95
  */
96
96
  eventID?: string
97
97
  /**
@@ -111,7 +111,7 @@ export interface Payload {
111
111
  */
112
112
  em?: string
113
113
  /**
114
- * Phone number of the user
114
+ * Phone number of the user. Make sure to include the country code. For example, "15551234567" for a US number.
115
115
  */
116
116
  ph?: string
117
117
  /**
@@ -139,7 +139,7 @@ export interface Payload {
139
139
  */
140
140
  st?: string
141
141
  /**
142
- * ZIP or postal code of the user. For example, "94025" for Menlo Park, CA, or "10001" for New York City.
142
+ * ZIP or postal code of the user. For example, U.S zip code: 94035, Australia zip code: 1987, France zip code: 75018, UK zip code: m11ae.
143
143
  */
144
144
  zp?: string
145
145
  /**
package/src/send/index.ts CHANGED
@@ -12,8 +12,8 @@ const action: BrowserActionDefinition<Settings, FBClient, Payload> = {
12
12
  platform: 'web',
13
13
  fields: AllFields,
14
14
  perform: (client, { payload, settings, analytics }) => {
15
- return send(client, payload, settings, analytics)
15
+ return send(client, payload, settings, analytics)
16
16
  }
17
17
  }
18
18
 
19
- export default action
19
+ export default action
package/src/types.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export interface WindowWithOptionalFbq extends Omit<Window, 'fbq' | '_fbq'> {
2
- fbq?: FBClient
3
- _fbq?: FBClient
2
+ fbq?: FBClient;
3
+ _fbq?: FBClient;
4
4
  }
5
5
 
6
6
  export type FBStandardEventType =
@@ -29,8 +29,8 @@ export type InitOptions = {
29
29
  agent?: string
30
30
  }
31
31
 
32
- export type EventOptions = {
33
- eventID?: string
32
+ export type EventOptions = {
33
+ eventID?: string
34
34
  eventSourceUrl?: string
35
35
  }
36
36
 
@@ -41,7 +41,7 @@ export type UserData = {
41
41
  ph?: string // Phone number (FB hashes with SHA-256)
42
42
  fn?: string // First name (FB hashes with SHA-256)
43
43
  ln?: string // Last name (FB hashes with SHA-256)
44
- ge?: string // Gender (FB hashes with SHA-256)
44
+ ge?: 'm' | 'f' // Gender (FB hashes with SHA-256)
45
45
  db?: string // Date of birth (FB hashes with SHA-256) - format: YYYYMMDD
46
46
  ct?: string // City (FB hashes with SHA-256)
47
47
  st?: string // State (FB hashes with SHA-256)
@@ -63,7 +63,7 @@ export type FBEvent = {
63
63
  currency?: string
64
64
  delivery_category?: string
65
65
  num_items?: number
66
- value?: number
66
+ value?: number
67
67
  custom_data?: {
68
68
  [k: string]: unknown
69
69
  }
@@ -78,26 +78,26 @@ export type FBClient = {
78
78
  callMethod?: (...args: unknown[]) => void
79
79
  (command: 'set', key: string, value: boolean, pixelId: string): void
80
80
  (command: 'dataProcessingOptions', options: string[], country?: number, state?: number): void
81
- (command: 'init', pixelId: string, userData?: UserData, options?: InitOptions): void
81
+ (command: 'init', pixelId: string, userData?: UserData, options?: InitOptions ): void
82
82
  (command: 'trackSingle', pixelId: string, event: FBStandardEventType, params?: FBEvent, options?: EventOptions): void
83
83
  (command: 'trackSingleCustom', pixelId: string, event: string, params?: FBEvent, options?: EventOptions): void
84
84
  }
85
85
 
86
86
  export const LDU = {
87
- Disabled: { key: 'Disabled', state: undefined, country: undefined },
88
- GeolocationLogic: { key: 'GeolocationLogic', state: 0, country: 0 },
89
- California: { key: 'California', state: 1000, country: 1 },
90
- Colorado: { key: 'Colorado', state: 1001, country: 1 },
91
- Connecticut: { key: 'Connecticut', state: 1002, country: 1 },
92
- Florida: { key: 'Florida', state: 1003, country: 1 },
93
- Oregon: { key: 'Oregon', state: 1004, country: 1 },
94
- Texas: { key: 'Texas', state: 1005, country: 1 },
95
- Montana: { key: 'Montana', state: 1006, country: 1 },
96
- Delaware: { key: 'Delaware', state: 1007, country: 1 },
97
- Nebraska: { key: 'Nebraska', state: 1008, country: 1 },
98
- NewHampshire: { key: 'NewHampshire', state: 1009, country: 1 },
99
- NewJersey: { key: 'NewJersey', state: 1010, country: 1 },
100
- Minnesota: { key: 'Minnesota', state: 1011, country: 1 }
87
+ Disabled: {key: 'Disabled', state: undefined, country: undefined},
88
+ GeolocationLogic: {key: 'GeolocationLogic', state: 0, country: 0},
89
+ California: {key: 'California', state: 1000, country: 1},
90
+ Colorado: {key: 'Colorado', state: 1001, country: 1},
91
+ Connecticut: {key: 'Connecticut', state: 1002, country: 1},
92
+ Florida: {key: 'Florida', state: 1003, country: 1},
93
+ Oregon: {key: 'Oregon', state: 1004, country: 1},
94
+ Texas: {key: 'Texas', state: 1005, country: 1},
95
+ Montana: {key: 'Montana', state: 1006, country: 1},
96
+ Delaware: {key: 'Delaware', state: 1007, country: 1},
97
+ Nebraska: {key: 'Nebraska', state: 1008, country: 1},
98
+ NewHampshire: {key: 'NewHampshire', state: 1009, country: 1},
99
+ NewJersey: {key: 'NewJersey', state: 1010, country: 1},
100
+ Minnesota: {key: 'Minnesota', state: 1011, country: 1}
101
101
  } as const
102
102
 
103
- export type LDU = typeof LDU[keyof typeof LDU]
103
+ export type LDU = typeof LDU[keyof typeof LDU]