@segment/analytics-browser-actions-facebook-conversions-api-web 1.0.1-staging-0104ee0bb.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.
Files changed (81) hide show
  1. package/README.md +31 -0
  2. package/dist/cjs/generated-types.d.ts +5 -0
  3. package/dist/cjs/generated-types.js +3 -0
  4. package/dist/cjs/generated-types.js.map +1 -0
  5. package/dist/cjs/index.d.ts +11 -0
  6. package/dist/cjs/index.js +191 -0
  7. package/dist/cjs/index.js.map +1 -0
  8. package/dist/cjs/init-script.d.ts +2 -0
  9. package/dist/cjs/init-script.js +41 -0
  10. package/dist/cjs/init-script.js.map +1 -0
  11. package/dist/cjs/send/constants.d.ts +3 -0
  12. package/dist/cjs/send/constants.js +484 -0
  13. package/dist/cjs/send/constants.js.map +1 -0
  14. package/dist/cjs/send/depends-on.d.ts +5 -0
  15. package/dist/cjs/send/depends-on.js +46 -0
  16. package/dist/cjs/send/depends-on.js.map +1 -0
  17. package/dist/cjs/send/fields.d.ts +39 -0
  18. package/dist/cjs/send/fields.js +383 -0
  19. package/dist/cjs/send/fields.js.map +1 -0
  20. package/dist/cjs/send/generated-types.d.ts +43 -0
  21. package/dist/cjs/send/generated-types.js +3 -0
  22. package/dist/cjs/send/generated-types.js.map +1 -0
  23. package/dist/cjs/send/index.d.ts +6 -0
  24. package/dist/cjs/send/index.js +37 -0
  25. package/dist/cjs/send/index.js.map +1 -0
  26. package/dist/cjs/send/utils.d.ts +8 -0
  27. package/dist/cjs/send/utils.js +79 -0
  28. package/dist/cjs/send/utils.js.map +1 -0
  29. package/dist/cjs/send/validate.d.ts +2 -0
  30. package/dist/cjs/send/validate.js +12 -0
  31. package/dist/cjs/send/validate.js.map +1 -0
  32. package/dist/cjs/types.d.ts +132 -0
  33. package/dist/cjs/types.js +30 -0
  34. package/dist/cjs/types.js.map +1 -0
  35. package/dist/esm/generated-types.d.ts +5 -0
  36. package/dist/esm/generated-types.js +2 -0
  37. package/dist/esm/generated-types.js.map +1 -0
  38. package/dist/esm/index.d.ts +11 -0
  39. package/dist/esm/index.js +187 -0
  40. package/dist/esm/index.js.map +1 -0
  41. package/dist/esm/init-script.d.ts +2 -0
  42. package/dist/esm/init-script.js +38 -0
  43. package/dist/esm/init-script.js.map +1 -0
  44. package/dist/esm/send/constants.d.ts +3 -0
  45. package/dist/esm/send/constants.js +481 -0
  46. package/dist/esm/send/constants.js.map +1 -0
  47. package/dist/esm/send/depends-on.d.ts +5 -0
  48. package/dist/esm/send/depends-on.js +41 -0
  49. package/dist/esm/send/depends-on.js.map +1 -0
  50. package/dist/esm/send/fields.d.ts +39 -0
  51. package/dist/esm/send/fields.js +380 -0
  52. package/dist/esm/send/fields.js.map +1 -0
  53. package/dist/esm/send/generated-types.d.ts +43 -0
  54. package/dist/esm/send/generated-types.js +2 -0
  55. package/dist/esm/send/generated-types.js.map +1 -0
  56. package/dist/esm/send/index.d.ts +6 -0
  57. package/dist/esm/send/index.js +35 -0
  58. package/dist/esm/send/index.js.map +1 -0
  59. package/dist/esm/send/utils.d.ts +8 -0
  60. package/dist/esm/send/utils.js +74 -0
  61. package/dist/esm/send/utils.js.map +1 -0
  62. package/dist/esm/send/validate.d.ts +2 -0
  63. package/dist/esm/send/validate.js +9 -0
  64. package/dist/esm/send/validate.js.map +1 -0
  65. package/dist/esm/types.d.ts +132 -0
  66. package/dist/esm/types.js +27 -0
  67. package/dist/esm/types.js.map +1 -0
  68. package/dist/tsconfig.tsbuildinfo +1 -0
  69. package/package.json +25 -0
  70. package/src/generated-types.ts +16 -0
  71. package/src/index.ts +205 -0
  72. package/src/init-script.ts +40 -0
  73. package/src/send/constants.ts +482 -0
  74. package/src/send/depends-on.ts +47 -0
  75. package/src/send/fields.ts +400 -0
  76. package/src/send/generated-types.ts +150 -0
  77. package/src/send/index.ts +64 -0
  78. package/src/send/utils.ts +90 -0
  79. package/src/send/validate.ts +15 -0
  80. package/src/types.ts +117 -0
  81. package/tsconfig.json +9 -0
@@ -0,0 +1,400 @@
1
+ import type { InputField } from '@segment/actions-core'
2
+ import { ACTION_SOURCES } from '../types'
3
+ import { getDependenciesFor } from './depends-on'
4
+ import { CURRENCY_ISO_CODES } from './constants'
5
+
6
+ export const event_config: InputField = {
7
+ label: 'Event Configuration',
8
+ description: 'Specify the type of Facebook Conversions API event to send.',
9
+ type: 'object',
10
+ required: true,
11
+ additionalProperties: false,
12
+ properties: {
13
+ event_name: {
14
+ label: 'Event Name',
15
+ description: "Facebook Conversions API Event Name to send. Select 'Custom Event' to send a non standard event.",
16
+ type: 'string',
17
+ required: true,
18
+ choices: [
19
+ { label: 'Custom Event', value: 'CustomEvent' },
20
+ { label: 'Add Payment Info', value: 'AddPaymentInfo' },
21
+ { label: 'Add To Cart', value: 'AddToCart' },
22
+ { label: 'Add To Wishlist', value: 'AddToWishlist' },
23
+ { label: 'Complete Registration', value: 'CompleteRegistration' },
24
+ { label: 'Contact', value: 'Contact' },
25
+ { label: 'Customize Product', value: 'CustomizeProduct' },
26
+ { label: 'Donate', value: 'Donate' },
27
+ { label: 'Find Location', value: 'FindLocation' },
28
+ { label: 'Initiate Checkout', value: 'InitiateCheckout' },
29
+ { label: 'Lead', value: 'Lead' },
30
+ { label: 'Purchase', value: 'Purchase' },
31
+ { label: 'Schedule', value: 'Schedule' },
32
+ { label: 'Search', value: 'Search' },
33
+ { label: 'Start Trial', value: 'StartTrial' },
34
+ { label: 'Submit Application', value: 'SubmitApplication' },
35
+ { label: 'Subscribe', value: 'Subscribe' },
36
+ { label: 'View Content', value: 'ViewContent' }
37
+ ]
38
+ },
39
+ custom_event_name:{
40
+ label: 'Custom Event Name',
41
+ description: 'Custom event name to send to Facebook',
42
+ type: 'string',
43
+ depends_on: getDependenciesFor('custom_event_name')
44
+ },
45
+ show_fields: {
46
+ label: 'Show all fields',
47
+ description: 'Show all fields, even those which are not relevant to the selected Event Name.',
48
+ type: 'boolean',
49
+ default: false
50
+ }
51
+ },
52
+ default: {
53
+ show_fields: false,
54
+ custom_event_name: {'@path': '$.event'}
55
+ }
56
+ }
57
+
58
+ export const content_category: InputField = {
59
+ label: 'Content Category',
60
+ description: 'The category of the content associated with the event.',
61
+ type: 'string',
62
+ default: { '@path': '$.properties.category' },
63
+ depends_on: getDependenciesFor('content_category')
64
+ }
65
+
66
+ export const content_ids: InputField = {
67
+ label: 'Content IDs',
68
+ description: "Product IDs associated with the event, such as SKUs (e.g. ['ABC123', 'XYZ789']). Accepts a single string value or array of strings.",
69
+ type: 'string',
70
+ multiple: true,
71
+ allowNull: false,
72
+ minimum: 1,
73
+ depends_on: getDependenciesFor('content_ids'),
74
+ required: {
75
+ match: 'all',
76
+ conditions: [
77
+ {
78
+ fieldKey: 'event_config.event_name',
79
+ operator: 'is',
80
+ value: ['AddToCart', 'Purchase', 'ViewContent']
81
+ },
82
+ {
83
+ fieldKey: 'contents',
84
+ operator: 'is_not',
85
+ value: undefined
86
+ },
87
+ {
88
+ fieldKey: 'contents',
89
+ operator: 'is_not',
90
+ value: ''
91
+ }
92
+ ]
93
+ }
94
+ }
95
+
96
+ export const content_name: InputField = {
97
+ label: 'Content Name',
98
+ description: 'The name of the page or product associated with the event.',
99
+ type: 'string',
100
+ default: { '@path': '$.properties.name' },
101
+ depends_on: getDependenciesFor('content_name')
102
+ }
103
+
104
+ export const content_type: InputField = {
105
+ label: 'Content Type',
106
+ description:
107
+ 'If the IDs being passed in content_ids or contents parameter are IDs of products, then the value should be product. If product group IDs are being passed, then the value should be product_group. If no content_type is provided, Meta will match the event to every item that has the same ID, independent of its type.',
108
+ type: 'string',
109
+ choices: [
110
+ { value: 'product', label: 'Product' },
111
+ { value: 'product_group', label: 'Product Group' }
112
+ ],
113
+ depends_on: getDependenciesFor('content_type')
114
+ }
115
+
116
+ export const contents: InputField = {
117
+ label: 'Contents',
118
+ description: 'A list of JSON objects that contain the product IDs associated with the event plus information about the products. ID and quantity are required fields.',
119
+ type: 'object',
120
+ multiple: true,
121
+ allowNull: false,
122
+ minimum: 1,
123
+ additionalProperties: true,
124
+ properties: {
125
+ id: {
126
+ label: 'ID',
127
+ description: 'The product ID of the purchased item.',
128
+ type: 'string',
129
+ required: true
130
+ },
131
+ quantity: {
132
+ label: 'Quantity',
133
+ description: 'The number of items purchased.',
134
+ type: 'integer',
135
+ required: true
136
+ },
137
+ item_price: {
138
+ label: 'Item Price',
139
+ description: 'The price of the item.',
140
+ type: 'number'
141
+ }
142
+ },
143
+ default: {
144
+ '@arrayPath': [
145
+ '$.properties.products',
146
+ {
147
+ id: { '@path': '$.id' },
148
+ quantity: { '@path': '$.quantity' },
149
+ item_price: { '@path': '$.price' }
150
+ }
151
+ ]
152
+ },
153
+ depends_on: getDependenciesFor('contents'),
154
+ required: {
155
+ match: 'all',
156
+ conditions: [
157
+ {
158
+ fieldKey: 'event_config.event_name',
159
+ operator: 'is',
160
+ value: ['AddToCart', 'Purchase', 'ViewContent']
161
+ },
162
+ {
163
+ fieldKey: 'content_ids',
164
+ operator: 'is_not',
165
+ value: undefined
166
+ },
167
+ {
168
+ fieldKey: 'content_ids',
169
+ operator: 'is_not',
170
+ value: ''
171
+ }
172
+ ]
173
+ }
174
+ }
175
+
176
+ export const currency: InputField = {
177
+ label: 'Currency',
178
+ description: 'The currency for the value specified. Currency must be a valid ISO 4217 three-digit currency code.',
179
+ type: 'string',
180
+ default: { '@path': '$.properties.currency' },
181
+ depends_on: getDependenciesFor('currency'),
182
+ choices:(() => {
183
+ return [...CURRENCY_ISO_CODES].map(code => ({
184
+ value: code,
185
+ label: code
186
+ }))
187
+ })(),
188
+ required: {
189
+ match: 'all',
190
+ conditions: [
191
+ {
192
+ fieldKey: 'event_config.event_name',
193
+ operator: 'is',
194
+ value: 'Purchase'
195
+ }
196
+ ]
197
+ }
198
+ }
199
+
200
+ export const delivery_category: InputField = {
201
+ label: 'Delivery Category',
202
+ description: 'Category of the delivery',
203
+ type: 'string',
204
+ choices: [
205
+ { value: 'in_store', label: 'In Store' },
206
+ { value: 'curbside', label: 'Curbside' },
207
+ { value: 'home_delivery', label: 'Home Delivery' }
208
+ ],
209
+ default: 'home_delivery',
210
+ depends_on: getDependenciesFor('delivery_category')
211
+ }
212
+
213
+ export const num_items: InputField = {
214
+ label: 'Number of Items',
215
+ description: 'The number of items when checkout was initiated.',
216
+ type: 'integer',
217
+ default: { '@path': '$.properties.quantity' },
218
+ depends_on: getDependenciesFor('num_items')
219
+ }
220
+
221
+ export const predicted_ltv: InputField = {
222
+ label: 'Predicted LTV',
223
+ description: 'Predicted lifetime value of a subscriber as defined by the advertiser and expressed as an exact value.',
224
+ type: 'number',
225
+ depends_on: getDependenciesFor('predicted_ltv')
226
+ }
227
+
228
+ export const search_string: InputField = {
229
+ label: 'Search String',
230
+ description: 'The string entered by the user for the search.',
231
+ type: 'string',
232
+ default: { '@path': '$.properties.query' },
233
+ depends_on: getDependenciesFor('search_string')
234
+ }
235
+
236
+ export const status: InputField = {
237
+ label: 'Registration Status',
238
+ description: 'The status of the registration. true for completed registrations, false otherwise.',
239
+ type: 'boolean',
240
+ depends_on: getDependenciesFor('status')
241
+ }
242
+
243
+ export const value: InputField = {
244
+ label: 'Value',
245
+ description: 'A numeric value associated with this event. This could be a monetary value or a value in some other metric.',
246
+ type: 'number',
247
+ default: { '@path': '$.properties.currency' },
248
+ depends_on: getDependenciesFor('value'),
249
+ required: {
250
+ match: 'all',
251
+ conditions: [
252
+ {
253
+ fieldKey: 'event_config.event_name',
254
+ operator: 'is',
255
+ value: 'Purchase'
256
+ }
257
+ ]
258
+ }
259
+ }
260
+
261
+ export const custom_data: InputField = {
262
+ label: 'Custom Data',
263
+ description: 'The custom data object can be used to pass custom properties.',
264
+ type: 'object',
265
+ additionalProperties: true,
266
+ defaultObjectUI: 'keyvalue'
267
+ }
268
+
269
+ export const eventID: InputField = {
270
+ label: 'Event ID',
271
+ description: 'This ID can be any unique string. Event ID is used to deduplicate events sent by both Facebook Pixel and Conversions API.',
272
+ type: 'string',
273
+ default: { '@path': '$.messageId' }
274
+ }
275
+
276
+ export const eventSourceUrl: InputField = {
277
+ label: 'Event Source URL',
278
+ description: 'The URL of the page where the event occurred. Can be used to override the default URL taken from the current page.',
279
+ type: 'string',
280
+ default: { '@path': '$.context.page.url' }
281
+ }
282
+
283
+ export const actionSource: InputField = {
284
+ label: 'Action Source',
285
+ description: 'The source of the event. This can be used to specify where the event originated from.',
286
+ type: 'string',
287
+ choices: [
288
+ { label: 'Email', value: ACTION_SOURCES.email },
289
+ { label: 'Website', value: ACTION_SOURCES.website },
290
+ { label: 'App', value: ACTION_SOURCES.app },
291
+ { label: 'Phone Call', value: ACTION_SOURCES.phone_call },
292
+ { label: 'Chat', value: ACTION_SOURCES.chat },
293
+ { label: 'Physical Store', value: ACTION_SOURCES.physical_store },
294
+ { label: 'System Generated', value: ACTION_SOURCES.system_generated },
295
+ { label: 'Other', value: ACTION_SOURCES.other }
296
+ ],
297
+ default: ACTION_SOURCES.website
298
+ }
299
+
300
+ export const userData: InputField = {
301
+ label: 'User Data',
302
+ description: 'User data to be sent with the event. This can include hashed identifiers like email, phone number, etc.',
303
+ type: 'object',
304
+ properties: {
305
+ external_id: {
306
+ label: 'External ID',
307
+ description: 'A unique identifier for the user from your system',
308
+ type: 'string'
309
+ },
310
+ em: {
311
+ label: 'Email',
312
+ description: 'Email address of the user',
313
+ type: 'string',
314
+ format: 'email'
315
+ },
316
+ ph: {
317
+ label: 'Phone Number',
318
+ description: 'Phone number of the user',
319
+ type: 'string'
320
+ },
321
+ fn: {
322
+ label: 'First Name',
323
+ description: 'First name of the user',
324
+ type: 'string'
325
+ },
326
+ ln: {
327
+ label: 'Last Name',
328
+ description: 'Last name of the user',
329
+ type: 'string'
330
+ },
331
+ ge: {
332
+ label: 'Gender',
333
+ description: 'Gender of the user. If unknown leave blank.',
334
+ type: 'string',
335
+ choices: [
336
+ { label: 'Male', value: 'm' },
337
+ { label: 'Female', value: 'f' }
338
+ ]
339
+ },
340
+ db: {
341
+ label: 'Date of Birth',
342
+ description: 'Date of birth of the user',
343
+ type: 'string'
344
+ },
345
+ ct: {
346
+ label: 'City',
347
+ description: 'City of the user',
348
+ type: 'string'
349
+ },
350
+ st: {
351
+ label: 'State',
352
+ description: 'State of the user. Two-letter state or province code for the United States, For example, "NY" for New York.',
353
+ type: 'string'
354
+ },
355
+ zp: {
356
+ label: 'ZIP/Postal Code',
357
+ description: 'ZIP or postal code of the user. For example, "94025" for Menlo Park, CA, or "10001" for New York City.',
358
+ type: 'string'
359
+ },
360
+ country: {
361
+ label: 'Country',
362
+ description: 'Country code of the user. This should be a valid ISO 3166-1 alpha-2 country code. For example, "US" for the United States.',
363
+ type: 'string'
364
+ }
365
+ },
366
+ default: {
367
+ external_id: { '@path': '$.context.traits.userId' },
368
+ em: { '@path': '$.context.traits.email' },
369
+ ph: { '@path': '$.context.traits.phone' },
370
+ fn: { '@path': '$.context.traits.first_name' },
371
+ ln: { '@path': '$.context.traits.last_name' },
372
+ ge: { '@path': '$.context.traits.gender' },
373
+ db: { '@path': '$.context.traits.birthday' },
374
+ ct: { '@path': '$.context.traits.address.city' },
375
+ st: { '@path': '$.context.traits.address.state' },
376
+ zp: { '@path': '$.context.traits.address.postal_code' },
377
+ country: { '@path': '$.context.traits.address.country' }
378
+ }
379
+ }
380
+
381
+ export const AllFields = {
382
+ event_config,
383
+ content_category,
384
+ content_ids,
385
+ content_name,
386
+ content_type,
387
+ contents,
388
+ currency,
389
+ delivery_category,
390
+ num_items,
391
+ predicted_ltv,
392
+ search_string,
393
+ status,
394
+ value,
395
+ custom_data,
396
+ eventID,
397
+ eventSourceUrl,
398
+ actionSource,
399
+ userData
400
+ }
@@ -0,0 +1,150 @@
1
+ // Generated file. DO NOT MODIFY IT BY HAND.
2
+
3
+ export interface Payload {
4
+ /**
5
+ * Specify the type of Facebook Conversions API event to send.
6
+ */
7
+ event_config: {
8
+ /**
9
+ * Facebook Conversions API Event Name to send. Select 'Custom Event' to send a non standard event.
10
+ */
11
+ event_name: string
12
+ /**
13
+ * Custom event name to send to Facebook
14
+ */
15
+ custom_event_name?: string
16
+ /**
17
+ * Show all fields, even those which are not relevant to the selected Event Name.
18
+ */
19
+ show_fields?: boolean
20
+ }
21
+ /**
22
+ * The category of the content associated with the event.
23
+ */
24
+ content_category?: string
25
+ /**
26
+ * Product IDs associated with the event, such as SKUs (e.g. ['ABC123', 'XYZ789']). Accepts a single string value or array of strings.
27
+ */
28
+ content_ids?: string[]
29
+ /**
30
+ * The name of the page or product associated with the event.
31
+ */
32
+ content_name?: string
33
+ /**
34
+ * If the IDs being passed in content_ids or contents parameter are IDs of products, then the value should be product. If product group IDs are being passed, then the value should be product_group. If no content_type is provided, Meta will match the event to every item that has the same ID, independent of its type.
35
+ */
36
+ content_type?: string
37
+ /**
38
+ * A list of JSON objects that contain the product IDs associated with the event plus information about the products. ID and quantity are required fields.
39
+ */
40
+ contents?: {
41
+ /**
42
+ * The product ID of the purchased item.
43
+ */
44
+ id: string
45
+ /**
46
+ * The number of items purchased.
47
+ */
48
+ quantity: number
49
+ /**
50
+ * The price of the item.
51
+ */
52
+ item_price?: number
53
+ [k: string]: unknown
54
+ }[]
55
+ /**
56
+ * The currency for the value specified. Currency must be a valid ISO 4217 three-digit currency code.
57
+ */
58
+ currency?: string
59
+ /**
60
+ * Category of the delivery
61
+ */
62
+ delivery_category?: string
63
+ /**
64
+ * The number of items when checkout was initiated.
65
+ */
66
+ num_items?: number
67
+ /**
68
+ * Predicted lifetime value of a subscriber as defined by the advertiser and expressed as an exact value.
69
+ */
70
+ predicted_ltv?: number
71
+ /**
72
+ * The string entered by the user for the search.
73
+ */
74
+ search_string?: string
75
+ /**
76
+ * The status of the registration. true for completed registrations, false otherwise.
77
+ */
78
+ status?: boolean
79
+ /**
80
+ * A numeric value associated with this event. This could be a monetary value or a value in some other metric.
81
+ */
82
+ value?: number
83
+ /**
84
+ * The custom data object can be used to pass custom properties.
85
+ */
86
+ custom_data?: {
87
+ [k: string]: unknown
88
+ }
89
+ /**
90
+ * This ID can be any unique string. Event ID is used to deduplicate events sent by both Facebook Pixel and Conversions API.
91
+ */
92
+ eventID?: string
93
+ /**
94
+ * The URL of the page where the event occurred. Can be used to override the default URL taken from the current page.
95
+ */
96
+ eventSourceUrl?: string
97
+ /**
98
+ * The source of the event. This can be used to specify where the event originated from.
99
+ */
100
+ actionSource?: string
101
+ /**
102
+ * User data to be sent with the event. This can include hashed identifiers like email, phone number, etc.
103
+ */
104
+ userData?: {
105
+ /**
106
+ * A unique identifier for the user from your system
107
+ */
108
+ external_id?: string
109
+ /**
110
+ * Email address of the user
111
+ */
112
+ em?: string
113
+ /**
114
+ * Phone number of the user
115
+ */
116
+ ph?: string
117
+ /**
118
+ * First name of the user
119
+ */
120
+ fn?: string
121
+ /**
122
+ * Last name of the user
123
+ */
124
+ ln?: string
125
+ /**
126
+ * Gender of the user. If unknown leave blank.
127
+ */
128
+ ge?: string
129
+ /**
130
+ * Date of birth of the user
131
+ */
132
+ db?: string
133
+ /**
134
+ * City of the user
135
+ */
136
+ ct?: string
137
+ /**
138
+ * State of the user. Two-letter state or province code for the United States, For example, "NY" for New York.
139
+ */
140
+ st?: string
141
+ /**
142
+ * ZIP or postal code of the user. For example, "94025" for Menlo Park, CA, or "10001" for New York City.
143
+ */
144
+ zp?: string
145
+ /**
146
+ * Country code of the user. This should be a valid ISO 3166-1 alpha-2 country code. For example, "US" for the United States.
147
+ */
148
+ country?: string
149
+ }
150
+ }
@@ -0,0 +1,64 @@
1
+ import type { BrowserActionDefinition } from '@segment/browser-destination-runtime/types'
2
+ import type { Settings } from '../generated-types'
3
+ import type { Payload } from './generated-types'
4
+ import { AllFields } from './fields'
5
+ import type { FBClient, FBStandardEventType, FBNonStandardEventType } from '../types'
6
+ import { buildOptions } from './utils'
7
+ import { getNotVisibleForEvent } from './depends-on'
8
+ import { validate } from './validate'
9
+
10
+ const action: BrowserActionDefinition<Settings, FBClient, Payload> = {
11
+ title: 'Send Event',
12
+ description: 'Send a Standard or Custom Event to Facebook Conversions API.',
13
+ platform: 'web',
14
+ fields: AllFields,
15
+ perform: (client, { payload, settings }) => {
16
+ const { pixelId } = settings
17
+ const {
18
+ event_config: { custom_event_name, show_fields, event_name } = {},
19
+ eventID,
20
+ eventSourceUrl,
21
+ actionSource,
22
+ userData,
23
+ ...rest
24
+ } = payload
25
+
26
+ const isCustom = event_name === 'CustomEvent' ? true : false
27
+
28
+ if(isCustom){
29
+ validate(payload)
30
+ }
31
+
32
+ if(show_fields === false){
33
+ // If show_fields is false we delete values for fields which are hidden in the UI.
34
+ const fieldsToDelete = getNotVisibleForEvent(event_name as FBStandardEventType | FBNonStandardEventType)
35
+ fieldsToDelete.forEach(field => {
36
+ if (field in rest) {
37
+ delete rest[field as keyof typeof rest]
38
+ }
39
+ })
40
+ }
41
+
42
+ const options = buildOptions(payload)
43
+
44
+ if(isCustom){
45
+ client(
46
+ 'trackSingleCustom',
47
+ pixelId,
48
+ custom_event_name as string,
49
+ { ...rest },
50
+ options
51
+ )
52
+ } else {
53
+ client(
54
+ 'trackSingle',
55
+ pixelId,
56
+ event_name as FBStandardEventType,
57
+ { ...rest },
58
+ options
59
+ )
60
+ }
61
+ }
62
+ }
63
+
64
+ export default action
@@ -0,0 +1,90 @@
1
+ import { LDU, UserData, Options, ActionSource } from '../types'
2
+ import { US_STATE_CODES, COUNTRY_CODES} from './constants'
3
+ import { Payload } from './generated-types'
4
+
5
+ export function getLDU(ldu: keyof typeof LDU) {
6
+ const lduObj = LDU[ldu]
7
+ return { country: lduObj.country, state: lduObj.state }
8
+ }
9
+
10
+ export function buildOptions(payload: Payload): Options | undefined {
11
+ const { eventID, eventSourceUrl, actionSource, userData } = payload
12
+
13
+ const options: Options = {
14
+ eventID,
15
+ eventSourceUrl,
16
+ actionSource: actionSource as ActionSource | undefined,
17
+ userData: santizeUserData(userData as UserData)
18
+ }
19
+
20
+ return Object.values(options).some(Boolean) ? options : undefined
21
+ }
22
+
23
+ export function santizeUserData(userData: UserData): UserData | undefined {
24
+ if(!userData){
25
+ return undefined
26
+ }
27
+ userData.em = typeof userData.em === 'string' ? userData.em.toLowerCase().trim() : undefined // lowercase and trim whitespace
28
+ userData.ph = userData.ph ? userData.ph.replace(/\D/g, '') : undefined // remove non-numeric characters
29
+ userData.fn = userData.fn ? userData.fn.toLowerCase().trim() : undefined // lowercase and trim whitespace
30
+ userData.ln = userData.ln ? userData.ln.toLowerCase().trim() : undefined // lowercase and trim whitespace
31
+ userData.ge = userData.ge ? userData.ge.toLowerCase().trim() : undefined // lowercase and trim whitespace
32
+ userData.db = formatDate(userData.db) // format date to YYYYMMDD
33
+ userData.ct = userData.ct ? userData.ct.toLowerCase().replace(/\s+/g, '') : undefined // lowercase and replace any whitespace
34
+ userData.st = sanitizeState(userData.st) // lowercase 2 character state code
35
+ userData.zp = userData.zp ? userData.zp.trim() : undefined
36
+ userData.country = sanitizeCountry(userData.country) // lowercase 2 character country code
37
+ return userData
38
+ }
39
+
40
+ function formatDate(isoDate?: string): string | undefined {
41
+ if (!isoDate || typeof isoDate !== 'string') {
42
+ return undefined
43
+ }
44
+ const date = new Date(isoDate)
45
+ if (isNaN(date.getTime())) {
46
+ return undefined
47
+ }
48
+ const year = date.getUTCFullYear()
49
+ const month = (date.getUTCMonth() + 1).toString().padStart(2, '0')
50
+ const day = date.getUTCDate().toString().padStart(2, '0')
51
+ return `${year}${month}${day}`
52
+ }
53
+
54
+ function sanitizeState(state?: string): string | undefined {
55
+ if (typeof state !== 'string' || !state.trim()) {
56
+ return undefined
57
+ }
58
+
59
+ const normalized = state.replace(/\s+/g, '').toLowerCase()
60
+
61
+ const abbreviation = US_STATE_CODES.get(normalized)
62
+ if (abbreviation) {
63
+ return abbreviation
64
+ }
65
+
66
+ if (/^[a-z]{2}$/i.test(normalized)) {
67
+ return normalized.toLowerCase()
68
+ }
69
+
70
+ return undefined
71
+ }
72
+
73
+ function sanitizeCountry(country?: string): string | undefined {
74
+ if (typeof country !== 'string' || !country.trim()) {
75
+ return undefined
76
+ }
77
+
78
+ const normalized = country.replace(/\s+/g, '').toUpperCase()
79
+
80
+ const abbreviation = COUNTRY_CODES.get(normalized)
81
+ if (abbreviation) {
82
+ return abbreviation
83
+ }
84
+
85
+ if (/^[A-Z]{2}$/.test(normalized)) {
86
+ return normalized.toLowerCase()
87
+ }
88
+
89
+ return undefined
90
+ }