@walkeros/server-destination-tiktok 3.3.0-next-1776098542393

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.
@@ -0,0 +1,101 @@
1
+ import { Mapping as Mapping$1, Destination as Destination$1 } from '@walkeros/core';
2
+ import { DestinationServer, sendServer } from '@walkeros/server-core';
3
+
4
+ interface Settings {
5
+ pixelCode: string;
6
+ accessToken: string;
7
+ url?: string;
8
+ test_event_code?: string;
9
+ doNotHash?: string[];
10
+ user_data?: Mapping$1.Map;
11
+ partner_name?: string;
12
+ }
13
+ type InitSettings = Partial<Settings>;
14
+ interface Mapping {
15
+ }
16
+ interface Env extends DestinationServer.Env {
17
+ sendServer?: typeof sendServer;
18
+ }
19
+ type Types = Destination$1.Types<Settings, Mapping, Env, InitSettings>;
20
+ interface Destination extends DestinationServer.Destination<Types> {
21
+ init: DestinationServer.InitFn<Types>;
22
+ }
23
+ type Config = {
24
+ settings: Settings;
25
+ } & DestinationServer.Config<Types>;
26
+ type InitFn = DestinationServer.InitFn<Types>;
27
+ type PushFn = DestinationServer.PushFn<Types>;
28
+ type PartialConfig = DestinationServer.PartialConfig<Types>;
29
+ type PushEvents = DestinationServer.PushEvents<Mapping>;
30
+ type Rule = Mapping$1.Rule<Mapping>;
31
+ type Rules = Mapping$1.Rules<Rule>;
32
+ interface RequestBody {
33
+ pixel_code: string;
34
+ partner_name?: string;
35
+ test_event_code?: string;
36
+ data: EventData[];
37
+ }
38
+ interface EventData {
39
+ event: string;
40
+ event_id: string;
41
+ timestamp: string;
42
+ context?: EventContext;
43
+ properties?: Record<string, unknown>;
44
+ }
45
+ interface EventContext {
46
+ ip?: string;
47
+ user_agent?: string;
48
+ user?: UserData;
49
+ page?: PageContext;
50
+ ad?: AdContext;
51
+ }
52
+ interface UserData {
53
+ email?: string;
54
+ phone_number?: string;
55
+ external_id?: string;
56
+ ttp?: string;
57
+ ttclid?: string;
58
+ locale?: string;
59
+ }
60
+ interface PageContext {
61
+ url?: string;
62
+ referrer?: string;
63
+ }
64
+ interface AdContext {
65
+ callback?: string;
66
+ }
67
+ interface ResponseBody {
68
+ code: number;
69
+ message: string;
70
+ data?: Record<string, unknown>;
71
+ }
72
+ type StandardEventName = 'ViewContent' | 'ClickButton' | 'Search' | 'AddToWishlist' | 'AddToCart' | 'InitiateCheckout' | 'AddPaymentInfo' | 'CompletePayment' | 'PlaceAnOrder' | 'Contact' | 'Download' | 'SubmitForm' | 'CompleteRegistration' | 'Subscribe' | (string & {});
73
+
74
+ type index_AdContext = AdContext;
75
+ type index_Config = Config;
76
+ type index_Destination = Destination;
77
+ type index_Env = Env;
78
+ type index_EventContext = EventContext;
79
+ type index_EventData = EventData;
80
+ type index_InitFn = InitFn;
81
+ type index_InitSettings = InitSettings;
82
+ type index_Mapping = Mapping;
83
+ type index_PageContext = PageContext;
84
+ type index_PartialConfig = PartialConfig;
85
+ type index_PushEvents = PushEvents;
86
+ type index_PushFn = PushFn;
87
+ type index_RequestBody = RequestBody;
88
+ type index_ResponseBody = ResponseBody;
89
+ type index_Rule = Rule;
90
+ type index_Rules = Rules;
91
+ type index_Settings = Settings;
92
+ type index_StandardEventName = StandardEventName;
93
+ type index_Types = Types;
94
+ type index_UserData = UserData;
95
+ declare namespace index {
96
+ export type { index_AdContext as AdContext, index_Config as Config, index_Destination as Destination, index_Env as Env, index_EventContext as EventContext, index_EventData as EventData, index_InitFn as InitFn, index_InitSettings as InitSettings, index_Mapping as Mapping, index_PageContext as PageContext, index_PartialConfig as PartialConfig, index_PushEvents as PushEvents, index_PushFn as PushFn, index_RequestBody as RequestBody, index_ResponseBody as ResponseBody, index_Rule as Rule, index_Rules as Rules, index_Settings as Settings, index_StandardEventName as StandardEventName, index_Types as Types, index_UserData as UserData };
97
+ }
98
+
99
+ declare const destinationTikTok: Destination;
100
+
101
+ export { index as DestinationTikTok, destinationTikTok as default, destinationTikTok };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";var mod,__defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,index_exports={};((target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})})(index_exports,{DestinationTikTok:()=>types_exports,default:()=>index_default,destinationTikTok:()=>destinationTikTok}),module.exports=(mod=index_exports,((to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to})(__defProp({},"__esModule",{value:!0}),mod));var import_core=require("@walkeros/core"),import_server_core2=require("@walkeros/server-core"),import_server_core=require("@walkeros/server-core"),keysToHash=["email","phone_number","external_id"];async function hashUserData(userData,doNotHash=[]){const entries=await Promise.all(Object.entries(userData).map(async([key,value])=>void 0===value?[key,value]:function(key,doNotHash=[]){return keysToHash.includes(key)&&!doNotHash.includes(key)}(key,doNotHash)?[key,await(0,import_server_core.getHashServer)(String(value))]:[key,value]));return Object.fromEntries(entries)}var types_exports={},destinationTikTok={type:"tiktok",config:{},async init({config:partialConfig,logger:logger}){const config=function(partialConfig={},logger){const settings=partialConfig.settings||{},{accessToken:accessToken,pixelCode:pixelCode}=settings;accessToken||logger.throw("Config settings accessToken missing"),pixelCode||logger.throw("Config settings pixelCode missing");const settingsConfig={partner_name:"walkerOS",...settings,accessToken:accessToken,pixelCode:pixelCode};return{...partialConfig,settings:settingsConfig}}(partialConfig,logger);return config},push:async(event,context)=>await async function(event,{config:config,rule:rule,data:data,collector:collector,env:env,logger:logger}){const{accessToken:accessToken,pixelCode:pixelCode,doNotHash:doNotHash,test_event_code:test_event_code,url:url="https://business-api.tiktok.com/open_api/v1.3/event/track/",user_data:user_data,partner_name:partner_name}=config.settings,eventData=(0,import_core.isObject)(data)?data:{},configData=config.data?await(0,import_core.getMappingValue)(event,config.data):{},userDataCustom=user_data?await(0,import_core.getMappingValue)(event,{map:user_data}):{},userData={...(0,import_core.isObject)(configData)&&(0,import_core.isObject)(configData.user_data)?configData.user_data:{},...(0,import_core.isObject)(userDataCustom)?userDataCustom:{},...(0,import_core.isObject)(eventData.user_data)?eventData.user_data:{}},properties={};if((0,import_core.isObject)(eventData))for(const[key,value]of Object.entries(eventData))"user_data"!==key&&(properties[key]=value);const hashedUserData=await hashUserData(userData,doNotHash),context={};Object.keys(hashedUserData).length>0&&(context.user=hashedUserData),context.page={url:event.source.id};const tiktokEvent={event:event.name,event_id:event.id,timestamp:new Date(event.timestamp).toISOString(),context:context,properties:properties},body={pixel_code:pixelCode,partner_name:partner_name,data:[tiktokEvent]};test_event_code&&(body.test_event_code=test_event_code),logger.debug("Calling TikTok Events API",{endpoint:url,method:"POST",eventName:tiktokEvent.event,eventId:tiktokEvent.event_id});const sendServerFn=(null==env?void 0:env.sendServer)||import_server_core2.sendServer,result=await sendServerFn(url,JSON.stringify(body),{headers:{"Access-Token":accessToken,"Content-Type":"application/json"}});logger.debug("TikTok API response",{ok:!(0,import_core.isObject)(result)||result.ok}),(0,import_core.isObject)(result)&&!1===result.ok&&logger.throw(`TikTok API error: ${JSON.stringify(result)}`)}(event,context)},index_default=destinationTikTok;//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/push.ts","../src/hash.ts","../src/types/index.ts"],"sourcesContent":["import type { Destination } from './types';\nimport { getConfig } from './config';\nimport { push } from './push';\n\n// Types\nexport * as DestinationTikTok from './types';\n\nexport const destinationTikTok: Destination = {\n type: 'tiktok',\n\n config: {},\n\n async init({ config: partialConfig, logger }) {\n const config = getConfig(partialConfig, logger);\n return config;\n },\n\n async push(event, context) {\n return await push(event, context);\n },\n};\n\nexport default destinationTikTok;\n","import type { Config, Settings, PartialConfig } from './types';\nimport type { Logger } from '@walkeros/core';\n\nexport function getConfig(\n partialConfig: PartialConfig = {},\n logger: Logger.Instance,\n): Config {\n const settings = (partialConfig.settings || {}) as Partial<Settings>;\n const { accessToken, pixelCode } = settings;\n\n if (!accessToken) logger.throw('Config settings accessToken missing');\n if (!pixelCode) logger.throw('Config settings pixelCode missing');\n\n const settingsConfig: Settings = {\n partner_name: 'walkerOS',\n ...settings,\n accessToken,\n pixelCode,\n };\n\n return { ...partialConfig, settings: settingsConfig };\n}\n","import type {\n EventData,\n EventContext,\n PushFn,\n RequestBody,\n UserData,\n Env,\n} from './types';\nimport { getMappingValue, isObject } from '@walkeros/core';\nimport { sendServer } from '@walkeros/server-core';\nimport { hashUserData } from './hash';\n\nexport const push: PushFn = async function (\n event,\n { config, rule, data, collector, env, logger },\n) {\n const {\n accessToken,\n pixelCode,\n doNotHash,\n test_event_code,\n url = 'https://business-api.tiktok.com/open_api/v1.3/event/track/',\n user_data,\n partner_name,\n } = config.settings as import('./types').Settings;\n\n const eventData = isObject(data) ? data : {};\n const configData = config.data\n ? await getMappingValue(event, config.data)\n : {};\n const userDataCustom = user_data\n ? await getMappingValue(event, { map: user_data })\n : {};\n\n // Build user data from multiple sources\n const userData: UserData = {\n // Destination config data\n ...(isObject(configData) && isObject(configData.user_data)\n ? (configData.user_data as UserData)\n : {}),\n // Custom user_data from settings\n ...(isObject(userDataCustom) ? (userDataCustom as UserData) : {}),\n // Event mapping data\n ...(isObject(eventData.user_data) ? (eventData.user_data as UserData) : {}),\n };\n\n // Build properties from event data, excluding user_data\n const properties: Record<string, unknown> = {};\n if (isObject(eventData)) {\n for (const [key, value] of Object.entries(eventData)) {\n if (key !== 'user_data') {\n properties[key] = value;\n }\n }\n }\n\n // Hash identity fields\n const hashedUserData = await hashUserData(userData, doNotHash);\n\n // Build context\n const context: EventContext = {};\n\n // Add user data if non-empty\n if (Object.keys(hashedUserData).length > 0) {\n context.user = hashedUserData;\n }\n\n // Page context from event source\n context.page = {\n url: event.source.id,\n };\n\n // Build event data\n const tiktokEvent: EventData = {\n event: event.name,\n event_id: event.id,\n timestamp: new Date(event.timestamp).toISOString(),\n context,\n properties,\n };\n\n // Build request body\n const body: RequestBody = {\n pixel_code: pixelCode,\n partner_name,\n data: [tiktokEvent],\n };\n\n // Test event code\n if (test_event_code) body.test_event_code = test_event_code;\n\n logger.debug('Calling TikTok Events API', {\n endpoint: url,\n method: 'POST',\n eventName: tiktokEvent.event,\n eventId: tiktokEvent.event_id,\n });\n\n const sendServerFn = (env as Env)?.sendServer || sendServer;\n const result = await sendServerFn(url, JSON.stringify(body), {\n headers: {\n 'Access-Token': accessToken,\n 'Content-Type': 'application/json',\n },\n });\n\n logger.debug('TikTok API response', {\n ok: isObject(result) ? result.ok : true,\n });\n\n if (isObject(result) && result.ok === false) {\n logger.throw(`TikTok API error: ${JSON.stringify(result)}`);\n }\n};\n","import type { UserData } from './types';\nimport { getHashServer } from '@walkeros/server-core';\n\nconst keysToHash = ['email', 'phone_number', 'external_id'];\n\nfunction shouldBeHashed(key: string, doNotHash: string[] = []): boolean {\n return keysToHash.includes(key) && !doNotHash.includes(key);\n}\n\nexport async function hashUserData(\n userData: UserData,\n doNotHash: string[] = [],\n): Promise<UserData> {\n const entries = await Promise.all(\n Object.entries(userData).map(async ([key, value]) => {\n if (value === undefined) return [key, value];\n if (shouldBeHashed(key, doNotHash)) {\n return [key, await getHashServer(String(value))];\n }\n return [key, value];\n }),\n );\n\n return Object.fromEntries(entries) as UserData;\n}\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationServer, sendServer } from '@walkeros/server-core';\n\nexport interface Settings {\n pixelCode: string;\n accessToken: string;\n url?: string;\n test_event_code?: string;\n doNotHash?: string[];\n user_data?: WalkerOSMapping.Map;\n partner_name?: string;\n}\n\nexport type InitSettings = Partial<Settings>;\n\nexport interface Mapping {}\n\nexport interface Env extends DestinationServer.Env {\n sendServer?: typeof sendServer;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport interface Destination extends DestinationServer.Destination<Types> {\n init: DestinationServer.InitFn<Types>;\n}\n\nexport type Config = {\n settings: Settings;\n} & DestinationServer.Config<Types>;\n\nexport type InitFn = DestinationServer.InitFn<Types>;\nexport type PushFn = DestinationServer.PushFn<Types>;\n\nexport type PartialConfig = DestinationServer.PartialConfig<Types>;\n\nexport type PushEvents = DestinationServer.PushEvents<Mapping>;\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n\n// TikTok Events API Request Body\nexport interface RequestBody {\n pixel_code: string;\n partner_name?: string;\n test_event_code?: string;\n data: EventData[];\n}\n\n// Individual event in the data array\nexport interface EventData {\n event: string;\n event_id: string;\n timestamp: string;\n context?: EventContext;\n properties?: Record<string, unknown>;\n}\n\n// Context object within each event\nexport interface EventContext {\n ip?: string;\n user_agent?: string;\n user?: UserData;\n page?: PageContext;\n ad?: AdContext;\n}\n\n// User identity fields\nexport interface UserData {\n email?: string;\n phone_number?: string;\n external_id?: string;\n ttp?: string;\n ttclid?: string;\n locale?: string;\n}\n\n// Page context\nexport interface PageContext {\n url?: string;\n referrer?: string;\n}\n\n// Ad attribution context\nexport interface AdContext {\n callback?: string;\n}\n\n// TikTok API response\nexport interface ResponseBody {\n code: number;\n message: string;\n data?: Record<string, unknown>;\n}\n\n// Standard TikTok event names\nexport type StandardEventName =\n | 'ViewContent'\n | 'ClickButton'\n | 'Search'\n | 'AddToWishlist'\n | 'AddToCart'\n | 'InitiateCheckout'\n | 'AddPaymentInfo'\n | 'CompletePayment'\n | 'PlaceAnOrder'\n | 'Contact'\n | 'Download'\n | 'SubmitForm'\n | 'CompleteRegistration'\n | 'Subscribe'\n | (string & {});\n"],"mappings":"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","names":["import_server_core"]}
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ import{getMappingValue,isObject}from"@walkeros/core";import{sendServer}from"@walkeros/server-core";import{getHashServer}from"@walkeros/server-core";var keysToHash=["email","phone_number","external_id"];async function hashUserData(userData,doNotHash=[]){const entries=await Promise.all(Object.entries(userData).map(async([key,value])=>void 0===value?[key,value]:function(key,doNotHash=[]){return keysToHash.includes(key)&&!doNotHash.includes(key)}(key,doNotHash)?[key,await getHashServer(String(value))]:[key,value]));return Object.fromEntries(entries)}var types_exports={},destinationTikTok={type:"tiktok",config:{},async init({config:partialConfig,logger:logger}){const config=function(partialConfig={},logger){const settings=partialConfig.settings||{},{accessToken:accessToken,pixelCode:pixelCode}=settings;accessToken||logger.throw("Config settings accessToken missing"),pixelCode||logger.throw("Config settings pixelCode missing");const settingsConfig={partner_name:"walkerOS",...settings,accessToken:accessToken,pixelCode:pixelCode};return{...partialConfig,settings:settingsConfig}}(partialConfig,logger);return config},push:async(event,context)=>await async function(event,{config:config,rule:rule,data:data,collector:collector,env:env,logger:logger}){const{accessToken:accessToken,pixelCode:pixelCode,doNotHash:doNotHash,test_event_code:test_event_code,url:url="https://business-api.tiktok.com/open_api/v1.3/event/track/",user_data:user_data,partner_name:partner_name}=config.settings,eventData=isObject(data)?data:{},configData=config.data?await getMappingValue(event,config.data):{},userDataCustom=user_data?await getMappingValue(event,{map:user_data}):{},userData={...isObject(configData)&&isObject(configData.user_data)?configData.user_data:{},...isObject(userDataCustom)?userDataCustom:{},...isObject(eventData.user_data)?eventData.user_data:{}},properties={};if(isObject(eventData))for(const[key,value]of Object.entries(eventData))"user_data"!==key&&(properties[key]=value);const hashedUserData=await hashUserData(userData,doNotHash),context={};Object.keys(hashedUserData).length>0&&(context.user=hashedUserData),context.page={url:event.source.id};const tiktokEvent={event:event.name,event_id:event.id,timestamp:new Date(event.timestamp).toISOString(),context:context,properties:properties},body={pixel_code:pixelCode,partner_name:partner_name,data:[tiktokEvent]};test_event_code&&(body.test_event_code=test_event_code),logger.debug("Calling TikTok Events API",{endpoint:url,method:"POST",eventName:tiktokEvent.event,eventId:tiktokEvent.event_id});const sendServerFn=(null==env?void 0:env.sendServer)||sendServer,result=await sendServerFn(url,JSON.stringify(body),{headers:{"Access-Token":accessToken,"Content-Type":"application/json"}});logger.debug("TikTok API response",{ok:!isObject(result)||result.ok}),isObject(result)&&!1===result.ok&&logger.throw(`TikTok API error: ${JSON.stringify(result)}`)}(event,context)},index_default=destinationTikTok;export{types_exports as DestinationTikTok,index_default as default,destinationTikTok};//# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts","../src/push.ts","../src/hash.ts","../src/types/index.ts","../src/index.ts"],"sourcesContent":["import type { Config, Settings, PartialConfig } from './types';\nimport type { Logger } from '@walkeros/core';\n\nexport function getConfig(\n partialConfig: PartialConfig = {},\n logger: Logger.Instance,\n): Config {\n const settings = (partialConfig.settings || {}) as Partial<Settings>;\n const { accessToken, pixelCode } = settings;\n\n if (!accessToken) logger.throw('Config settings accessToken missing');\n if (!pixelCode) logger.throw('Config settings pixelCode missing');\n\n const settingsConfig: Settings = {\n partner_name: 'walkerOS',\n ...settings,\n accessToken,\n pixelCode,\n };\n\n return { ...partialConfig, settings: settingsConfig };\n}\n","import type {\n EventData,\n EventContext,\n PushFn,\n RequestBody,\n UserData,\n Env,\n} from './types';\nimport { getMappingValue, isObject } from '@walkeros/core';\nimport { sendServer } from '@walkeros/server-core';\nimport { hashUserData } from './hash';\n\nexport const push: PushFn = async function (\n event,\n { config, rule, data, collector, env, logger },\n) {\n const {\n accessToken,\n pixelCode,\n doNotHash,\n test_event_code,\n url = 'https://business-api.tiktok.com/open_api/v1.3/event/track/',\n user_data,\n partner_name,\n } = config.settings as import('./types').Settings;\n\n const eventData = isObject(data) ? data : {};\n const configData = config.data\n ? await getMappingValue(event, config.data)\n : {};\n const userDataCustom = user_data\n ? await getMappingValue(event, { map: user_data })\n : {};\n\n // Build user data from multiple sources\n const userData: UserData = {\n // Destination config data\n ...(isObject(configData) && isObject(configData.user_data)\n ? (configData.user_data as UserData)\n : {}),\n // Custom user_data from settings\n ...(isObject(userDataCustom) ? (userDataCustom as UserData) : {}),\n // Event mapping data\n ...(isObject(eventData.user_data) ? (eventData.user_data as UserData) : {}),\n };\n\n // Build properties from event data, excluding user_data\n const properties: Record<string, unknown> = {};\n if (isObject(eventData)) {\n for (const [key, value] of Object.entries(eventData)) {\n if (key !== 'user_data') {\n properties[key] = value;\n }\n }\n }\n\n // Hash identity fields\n const hashedUserData = await hashUserData(userData, doNotHash);\n\n // Build context\n const context: EventContext = {};\n\n // Add user data if non-empty\n if (Object.keys(hashedUserData).length > 0) {\n context.user = hashedUserData;\n }\n\n // Page context from event source\n context.page = {\n url: event.source.id,\n };\n\n // Build event data\n const tiktokEvent: EventData = {\n event: event.name,\n event_id: event.id,\n timestamp: new Date(event.timestamp).toISOString(),\n context,\n properties,\n };\n\n // Build request body\n const body: RequestBody = {\n pixel_code: pixelCode,\n partner_name,\n data: [tiktokEvent],\n };\n\n // Test event code\n if (test_event_code) body.test_event_code = test_event_code;\n\n logger.debug('Calling TikTok Events API', {\n endpoint: url,\n method: 'POST',\n eventName: tiktokEvent.event,\n eventId: tiktokEvent.event_id,\n });\n\n const sendServerFn = (env as Env)?.sendServer || sendServer;\n const result = await sendServerFn(url, JSON.stringify(body), {\n headers: {\n 'Access-Token': accessToken,\n 'Content-Type': 'application/json',\n },\n });\n\n logger.debug('TikTok API response', {\n ok: isObject(result) ? result.ok : true,\n });\n\n if (isObject(result) && result.ok === false) {\n logger.throw(`TikTok API error: ${JSON.stringify(result)}`);\n }\n};\n","import type { UserData } from './types';\nimport { getHashServer } from '@walkeros/server-core';\n\nconst keysToHash = ['email', 'phone_number', 'external_id'];\n\nfunction shouldBeHashed(key: string, doNotHash: string[] = []): boolean {\n return keysToHash.includes(key) && !doNotHash.includes(key);\n}\n\nexport async function hashUserData(\n userData: UserData,\n doNotHash: string[] = [],\n): Promise<UserData> {\n const entries = await Promise.all(\n Object.entries(userData).map(async ([key, value]) => {\n if (value === undefined) return [key, value];\n if (shouldBeHashed(key, doNotHash)) {\n return [key, await getHashServer(String(value))];\n }\n return [key, value];\n }),\n );\n\n return Object.fromEntries(entries) as UserData;\n}\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationServer, sendServer } from '@walkeros/server-core';\n\nexport interface Settings {\n pixelCode: string;\n accessToken: string;\n url?: string;\n test_event_code?: string;\n doNotHash?: string[];\n user_data?: WalkerOSMapping.Map;\n partner_name?: string;\n}\n\nexport type InitSettings = Partial<Settings>;\n\nexport interface Mapping {}\n\nexport interface Env extends DestinationServer.Env {\n sendServer?: typeof sendServer;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport interface Destination extends DestinationServer.Destination<Types> {\n init: DestinationServer.InitFn<Types>;\n}\n\nexport type Config = {\n settings: Settings;\n} & DestinationServer.Config<Types>;\n\nexport type InitFn = DestinationServer.InitFn<Types>;\nexport type PushFn = DestinationServer.PushFn<Types>;\n\nexport type PartialConfig = DestinationServer.PartialConfig<Types>;\n\nexport type PushEvents = DestinationServer.PushEvents<Mapping>;\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n\n// TikTok Events API Request Body\nexport interface RequestBody {\n pixel_code: string;\n partner_name?: string;\n test_event_code?: string;\n data: EventData[];\n}\n\n// Individual event in the data array\nexport interface EventData {\n event: string;\n event_id: string;\n timestamp: string;\n context?: EventContext;\n properties?: Record<string, unknown>;\n}\n\n// Context object within each event\nexport interface EventContext {\n ip?: string;\n user_agent?: string;\n user?: UserData;\n page?: PageContext;\n ad?: AdContext;\n}\n\n// User identity fields\nexport interface UserData {\n email?: string;\n phone_number?: string;\n external_id?: string;\n ttp?: string;\n ttclid?: string;\n locale?: string;\n}\n\n// Page context\nexport interface PageContext {\n url?: string;\n referrer?: string;\n}\n\n// Ad attribution context\nexport interface AdContext {\n callback?: string;\n}\n\n// TikTok API response\nexport interface ResponseBody {\n code: number;\n message: string;\n data?: Record<string, unknown>;\n}\n\n// Standard TikTok event names\nexport type StandardEventName =\n | 'ViewContent'\n | 'ClickButton'\n | 'Search'\n | 'AddToWishlist'\n | 'AddToCart'\n | 'InitiateCheckout'\n | 'AddPaymentInfo'\n | 'CompletePayment'\n | 'PlaceAnOrder'\n | 'Contact'\n | 'Download'\n | 'SubmitForm'\n | 'CompleteRegistration'\n | 'Subscribe'\n | (string & {});\n","import type { Destination } from './types';\nimport { getConfig } from './config';\nimport { push } from './push';\n\n// Types\nexport * as DestinationTikTok from './types';\n\nexport const destinationTikTok: Destination = {\n type: 'tiktok',\n\n config: {},\n\n async init({ config: partialConfig, logger }) {\n const config = getConfig(partialConfig, logger);\n return config;\n },\n\n async push(event, context) {\n return await push(event, context);\n },\n};\n\nexport default destinationTikTok;\n"],"mappings":"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","names":[]}
@@ -0,0 +1,508 @@
1
+ {
2
+ "$meta": {
3
+ "package": "@walkeros/server-destination-tiktok",
4
+ "version": "0.1.0",
5
+ "type": "destination",
6
+ "platform": [
7
+ "server"
8
+ ],
9
+ "docs": "https://www.walkeros.io/docs/destinations/server/tiktok",
10
+ "source": "https://github.com/elbwalker/walkerOS/tree/main/packages/server/destinations/tiktok/src"
11
+ },
12
+ "schemas": {
13
+ "mapping": {
14
+ "$schema": "http://json-schema.org/draft-07/schema#",
15
+ "type": "object",
16
+ "properties": {},
17
+ "additionalProperties": false
18
+ },
19
+ "settings": {
20
+ "$schema": "http://json-schema.org/draft-07/schema#",
21
+ "type": "object",
22
+ "properties": {
23
+ "pixelCode": {
24
+ "type": "string",
25
+ "minLength": 1,
26
+ "description": "TikTok Pixel Code from Events Manager (like C0ABCDEF12345)"
27
+ },
28
+ "accessToken": {
29
+ "type": "string",
30
+ "minLength": 1,
31
+ "description": "Events API access token from TikTok Events Manager"
32
+ },
33
+ "url": {
34
+ "type": "string",
35
+ "format": "uri",
36
+ "description": "Custom Events API endpoint URL"
37
+ },
38
+ "test_event_code": {
39
+ "type": "string",
40
+ "description": "Test event code for debugging in TikTok Events Manager (like TEST12345)"
41
+ },
42
+ "doNotHash": {
43
+ "type": "array",
44
+ "items": {
45
+ "type": "string"
46
+ },
47
+ "description": "User data fields to skip hashing (like ['email', 'phone_number'])"
48
+ },
49
+ "user_data": {
50
+ "type": "object",
51
+ "propertyNames": {
52
+ "type": "string"
53
+ },
54
+ "additionalProperties": {
55
+ "type": "string"
56
+ },
57
+ "description": "Mapping for user data fields (like { email: 'user.email' })"
58
+ },
59
+ "partner_name": {
60
+ "type": "string",
61
+ "description": "Partner name for TikTok attribution"
62
+ }
63
+ },
64
+ "required": [
65
+ "pixelCode",
66
+ "accessToken"
67
+ ],
68
+ "additionalProperties": false
69
+ }
70
+ },
71
+ "examples": {
72
+ "env": {
73
+ "push": {
74
+ "sendServer": {
75
+ "$code": "async function(e,t,a){return{ok:!0,data:{code:0,message:\"OK\"}}}"
76
+ }
77
+ },
78
+ "simulation": [
79
+ "sendServer"
80
+ ]
81
+ },
82
+ "step": {
83
+ "addToCart": {
84
+ "in": {
85
+ "name": "product add",
86
+ "data": {
87
+ "id": "SKU-B2",
88
+ "name": "Running Shoes",
89
+ "price": 89.99,
90
+ "color": "blue"
91
+ },
92
+ "context": {
93
+ "shopping": [
94
+ "intent",
95
+ 0
96
+ ]
97
+ },
98
+ "globals": {
99
+ "pagegroup": "shop"
100
+ },
101
+ "custom": {
102
+ "completely": "random"
103
+ },
104
+ "user": {
105
+ "id": "us3r",
106
+ "device": "c00k13",
107
+ "session": "s3ss10n"
108
+ },
109
+ "nested": [
110
+ {
111
+ "entity": "product",
112
+ "data": {
113
+ "id": "SKU-B2",
114
+ "name": "Running Shoes",
115
+ "price": 89.99,
116
+ "quantity": 1
117
+ }
118
+ }
119
+ ],
120
+ "consent": {
121
+ "functional": true
122
+ },
123
+ "id": "1700000901000-gr0up-1",
124
+ "trigger": "click",
125
+ "entity": "product",
126
+ "action": "add",
127
+ "timestamp": 1700000901000,
128
+ "timing": 3.14,
129
+ "group": "gr0up",
130
+ "count": 1,
131
+ "version": {
132
+ "source": "3.2.0",
133
+ "tagging": 1
134
+ },
135
+ "source": {
136
+ "type": "server",
137
+ "id": "https://shop.example.com/products/running-shoes",
138
+ "previous_id": ""
139
+ }
140
+ },
141
+ "mapping": {
142
+ "name": "AddToCart",
143
+ "data": {
144
+ "map": {
145
+ "content_type": {
146
+ "value": "product"
147
+ },
148
+ "value": "data.price",
149
+ "currency": {
150
+ "value": "EUR"
151
+ },
152
+ "contents": {
153
+ "loop": [
154
+ "nested",
155
+ {
156
+ "condition": {
157
+ "$code": "e=>h(e)&&\"product\"===e.entity"
158
+ },
159
+ "map": {
160
+ "content_id": "data.id",
161
+ "content_name": "data.name",
162
+ "quantity": {
163
+ "key": "data.quantity",
164
+ "value": 1
165
+ },
166
+ "price": "data.price"
167
+ }
168
+ }
169
+ ]
170
+ }
171
+ }
172
+ }
173
+ },
174
+ "out": {
175
+ "pixel_code": "PIXEL_CODE",
176
+ "partner_name": "walkerOS",
177
+ "data": [
178
+ {
179
+ "event": "AddToCart",
180
+ "event_id": "1700000901000-gr0up-1",
181
+ "timestamp": "2023-11-14T22:28:21.000Z",
182
+ "context": {
183
+ "page": {
184
+ "url": "https://shop.example.com/products/running-shoes"
185
+ }
186
+ },
187
+ "properties": {
188
+ "content_type": "product",
189
+ "value": 89.99,
190
+ "currency": "EUR",
191
+ "contents": [
192
+ {
193
+ "content_id": "SKU-B2",
194
+ "content_name": "Running Shoes",
195
+ "quantity": 1,
196
+ "price": 89.99
197
+ }
198
+ ]
199
+ }
200
+ }
201
+ ]
202
+ }
203
+ },
204
+ "lead": {
205
+ "in": {
206
+ "name": "form submit",
207
+ "data": {
208
+ "type": "newsletter"
209
+ },
210
+ "context": {
211
+ "dev": [
212
+ "test",
213
+ 1
214
+ ]
215
+ },
216
+ "globals": {
217
+ "lang": "elb"
218
+ },
219
+ "custom": {
220
+ "completely": "random"
221
+ },
222
+ "user": {
223
+ "email": "user@example.com"
224
+ },
225
+ "nested": [
226
+ {
227
+ "entity": "child",
228
+ "data": {
229
+ "is": "subordinated"
230
+ },
231
+ "nested": [],
232
+ "context": {
233
+ "element": [
234
+ "child",
235
+ 0
236
+ ]
237
+ }
238
+ }
239
+ ],
240
+ "consent": {
241
+ "functional": true
242
+ },
243
+ "id": "1700000903000-gr0up-1",
244
+ "trigger": "test",
245
+ "entity": "form",
246
+ "action": "submit",
247
+ "timestamp": 1700000903000,
248
+ "timing": 3.14,
249
+ "group": "gr0up",
250
+ "count": 1,
251
+ "version": {
252
+ "source": "3.2.0",
253
+ "tagging": 1
254
+ },
255
+ "source": {
256
+ "type": "server",
257
+ "id": "https://example.com/contact",
258
+ "previous_id": ""
259
+ }
260
+ },
261
+ "mapping": {
262
+ "name": "SubmitForm",
263
+ "data": {
264
+ "map": {
265
+ "user_data": {
266
+ "map": {
267
+ "email": "user.email"
268
+ }
269
+ }
270
+ }
271
+ }
272
+ },
273
+ "out": {
274
+ "pixel_code": "PIXEL_CODE",
275
+ "partner_name": "walkerOS",
276
+ "data": [
277
+ {
278
+ "event": "SubmitForm",
279
+ "event_id": "1700000903000-gr0up-1",
280
+ "timestamp": "2023-11-14T22:28:23.000Z",
281
+ "context": {
282
+ "user": {
283
+ "email": "user@example.com"
284
+ },
285
+ "page": {
286
+ "url": "https://example.com/contact"
287
+ }
288
+ },
289
+ "properties": {}
290
+ }
291
+ ]
292
+ }
293
+ },
294
+ "pageView": {
295
+ "in": {
296
+ "name": "page view",
297
+ "data": {
298
+ "domain": "www.example.com",
299
+ "title": "walkerOS documentation",
300
+ "referrer": "https://www.walkeros.io/",
301
+ "search": "?foo=bar",
302
+ "hash": "#hash",
303
+ "id": "/docs/"
304
+ },
305
+ "context": {
306
+ "dev": [
307
+ "test",
308
+ 1
309
+ ]
310
+ },
311
+ "globals": {
312
+ "pagegroup": "docs"
313
+ },
314
+ "custom": {
315
+ "completely": "random"
316
+ },
317
+ "user": {
318
+ "id": "us3r",
319
+ "device": "c00k13",
320
+ "session": "s3ss10n"
321
+ },
322
+ "nested": [
323
+ {
324
+ "entity": "child",
325
+ "data": {
326
+ "is": "subordinated"
327
+ },
328
+ "nested": [],
329
+ "context": {
330
+ "element": [
331
+ "child",
332
+ 0
333
+ ]
334
+ }
335
+ }
336
+ ],
337
+ "consent": {
338
+ "functional": true
339
+ },
340
+ "id": "1700000902000-gr0up-1",
341
+ "trigger": "load",
342
+ "entity": "page",
343
+ "action": "view",
344
+ "timestamp": 1700000902000,
345
+ "timing": 3.14,
346
+ "group": "gr0up",
347
+ "count": 1,
348
+ "version": {
349
+ "source": "3.2.0",
350
+ "tagging": 1
351
+ },
352
+ "source": {
353
+ "type": "server",
354
+ "id": "https://example.com/docs/",
355
+ "previous_id": ""
356
+ }
357
+ },
358
+ "out": {
359
+ "pixel_code": "PIXEL_CODE",
360
+ "partner_name": "walkerOS",
361
+ "data": [
362
+ {
363
+ "event": "page view",
364
+ "event_id": "1700000902000-gr0up-1",
365
+ "timestamp": "2023-11-14T22:28:22.000Z",
366
+ "context": {
367
+ "page": {
368
+ "url": "https://example.com/docs/"
369
+ }
370
+ },
371
+ "properties": {}
372
+ }
373
+ ]
374
+ }
375
+ },
376
+ "purchase": {
377
+ "in": {
378
+ "name": "order complete",
379
+ "data": {
380
+ "id": "ORD-300",
381
+ "total": 249.99,
382
+ "currency": "EUR"
383
+ },
384
+ "context": {
385
+ "shopping": [
386
+ "complete",
387
+ 0
388
+ ]
389
+ },
390
+ "globals": {
391
+ "pagegroup": "shop"
392
+ },
393
+ "custom": {
394
+ "completely": "random"
395
+ },
396
+ "user": {
397
+ "id": "user-123",
398
+ "device": "device-456"
399
+ },
400
+ "nested": [
401
+ {
402
+ "entity": "product",
403
+ "data": {
404
+ "id": "SKU-A1",
405
+ "name": "Widget Pro",
406
+ "price": 124.99,
407
+ "quantity": 2
408
+ }
409
+ }
410
+ ],
411
+ "consent": {
412
+ "functional": true
413
+ },
414
+ "id": "1700000900000-gr0up-1",
415
+ "trigger": "load",
416
+ "entity": "order",
417
+ "action": "complete",
418
+ "timestamp": 1700000900000,
419
+ "timing": 3.14,
420
+ "group": "gr0up",
421
+ "count": 1,
422
+ "version": {
423
+ "source": "3.2.0",
424
+ "tagging": 1
425
+ },
426
+ "source": {
427
+ "type": "server",
428
+ "id": "https://shop.example.com/checkout/complete",
429
+ "previous_id": ""
430
+ }
431
+ },
432
+ "mapping": {
433
+ "name": "CompletePayment",
434
+ "data": {
435
+ "map": {
436
+ "value": "data.total",
437
+ "currency": {
438
+ "key": "data.currency",
439
+ "value": "EUR"
440
+ },
441
+ "order_id": "data.id",
442
+ "content_type": {
443
+ "value": "product"
444
+ },
445
+ "contents": {
446
+ "loop": [
447
+ "nested",
448
+ {
449
+ "condition": {
450
+ "$code": "e=>h(e)&&\"product\"===e.entity"
451
+ },
452
+ "map": {
453
+ "content_id": "data.id",
454
+ "content_name": "data.name",
455
+ "quantity": {
456
+ "key": "data.quantity",
457
+ "value": 1
458
+ },
459
+ "price": "data.price"
460
+ }
461
+ }
462
+ ]
463
+ },
464
+ "user_data": {
465
+ "map": {
466
+ "external_id": "user.id"
467
+ }
468
+ }
469
+ }
470
+ }
471
+ },
472
+ "out": {
473
+ "pixel_code": "PIXEL_CODE",
474
+ "partner_name": "walkerOS",
475
+ "data": [
476
+ {
477
+ "event": "CompletePayment",
478
+ "event_id": "1700000900000-gr0up-1",
479
+ "timestamp": "2023-11-14T22:28:20.000Z",
480
+ "context": {
481
+ "user": {
482
+ "external_id": "user-123"
483
+ },
484
+ "page": {
485
+ "url": "https://shop.example.com/checkout/complete"
486
+ }
487
+ },
488
+ "properties": {
489
+ "value": 249.99,
490
+ "currency": "EUR",
491
+ "order_id": "ORD-300",
492
+ "content_type": "product",
493
+ "contents": [
494
+ {
495
+ "content_id": "SKU-A1",
496
+ "content_name": "Widget Pro",
497
+ "quantity": 2,
498
+ "price": 124.99
499
+ }
500
+ ]
501
+ }
502
+ }
503
+ ]
504
+ }
505
+ }
506
+ }
507
+ }
508
+ }