@walkeros/server-destination-twitter 3.4.0-next-1776749829492

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,176 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/examples/index.ts
21
+ var examples_exports = {};
22
+ __export(examples_exports, {
23
+ env: () => env_exports,
24
+ step: () => step_exports
25
+ });
26
+ module.exports = __toCommonJS(examples_exports);
27
+
28
+ // src/examples/env.ts
29
+ var env_exports = {};
30
+ __export(env_exports, {
31
+ push: () => push,
32
+ simulation: () => simulation
33
+ });
34
+ var mockSendServer = async () => {
35
+ return {
36
+ ok: true,
37
+ data: { request_id: "mock-request-id" }
38
+ };
39
+ };
40
+ var push = {
41
+ sendServer: mockSendServer
42
+ };
43
+ var simulation = ["sendServer"];
44
+
45
+ // src/examples/step.ts
46
+ var step_exports = {};
47
+ __export(step_exports, {
48
+ lead: () => lead,
49
+ purchase: () => purchase,
50
+ purchaseWithTwclid: () => purchaseWithTwclid
51
+ });
52
+ var import_core = require("@walkeros/core");
53
+ var ENDPOINT = "https://ads-api.x.com/12/measurement/conversions/o8z6j";
54
+ var HEADERS = {
55
+ headers: {
56
+ Authorization: "<OAUTH_SIGNATURE>",
57
+ "Content-Type": "application/json"
58
+ }
59
+ };
60
+ var purchase = {
61
+ in: (0, import_core.getEvent)("order complete", {
62
+ timestamp: 17000009e5,
63
+ data: { total: 249.99, currency: "EUR" },
64
+ user: { email: "jane@example.com" },
65
+ source: { type: "server", id: "https://shop.example.com", previous_id: "" }
66
+ }),
67
+ mapping: {
68
+ settings: {
69
+ value: "data.total",
70
+ currency: { key: "data.currency", value: "EUR" }
71
+ }
72
+ },
73
+ out: [
74
+ [
75
+ "sendServer",
76
+ ENDPOINT,
77
+ JSON.stringify({
78
+ conversions: [
79
+ {
80
+ conversion_time: "2023-11-14T22:28:20.000Z",
81
+ event_id: "tw-o8z6j-o8z21",
82
+ identifiers: [
83
+ {
84
+ hashed_email: "8c87b489ce35cf2e2f39f80e282cb2e804932a56a213983eeeb428407d43b52d"
85
+ }
86
+ ],
87
+ conversion_id: "1700000900000-gr0up-1",
88
+ value: "249.99"
89
+ }
90
+ ]
91
+ }),
92
+ HEADERS
93
+ ]
94
+ ]
95
+ };
96
+ var lead = {
97
+ in: (0, import_core.getEvent)("form submit", {
98
+ timestamp: 1700000901e3,
99
+ user: { email: "user@example.com" },
100
+ source: { type: "server", id: "https://example.com", previous_id: "" }
101
+ }),
102
+ mapping: void 0,
103
+ out: [
104
+ [
105
+ "sendServer",
106
+ ENDPOINT,
107
+ JSON.stringify({
108
+ conversions: [
109
+ {
110
+ conversion_time: "2023-11-14T22:28:21.000Z",
111
+ event_id: "tw-o8z6j-o8z21",
112
+ identifiers: [
113
+ {
114
+ hashed_email: "b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514"
115
+ }
116
+ ],
117
+ conversion_id: "1700000901000-gr0up-1"
118
+ }
119
+ ]
120
+ }),
121
+ HEADERS
122
+ ]
123
+ ]
124
+ };
125
+ var purchaseWithTwclid = {
126
+ in: (0, import_core.getEvent)("order complete", {
127
+ timestamp: 1700000902e3,
128
+ data: { total: 89.99, currency: "USD" },
129
+ user: { email: "buyer@co.com" },
130
+ context: { twclid: ["23opevjt88psuo13lu8d020qkn", 0] },
131
+ source: { type: "server", id: "https://shop.example.com", previous_id: "" }
132
+ }),
133
+ mapping: {
134
+ settings: {
135
+ value: "data.total",
136
+ currency: { key: "data.currency", value: "USD" }
137
+ },
138
+ data: {
139
+ map: {
140
+ user_data: {
141
+ map: {
142
+ twclid: "context.twclid"
143
+ }
144
+ }
145
+ }
146
+ }
147
+ },
148
+ out: [
149
+ [
150
+ "sendServer",
151
+ ENDPOINT,
152
+ JSON.stringify({
153
+ conversions: [
154
+ {
155
+ conversion_time: "2023-11-14T22:28:22.000Z",
156
+ event_id: "tw-o8z6j-o8z21",
157
+ identifiers: [
158
+ {
159
+ hashed_email: "484c39bfb51212665d9673805c112b5ba04cbf0460b6d3f00bcdc18b92afed66"
160
+ },
161
+ { twclid: "23opevjt88psuo13lu8d020qkn" }
162
+ ],
163
+ conversion_id: "1700000902000-gr0up-1",
164
+ value: "89.99"
165
+ }
166
+ ]
167
+ }),
168
+ HEADERS
169
+ ]
170
+ ]
171
+ };
172
+ // Annotate the CommonJS export names for ESM import in node:
173
+ 0 && (module.exports = {
174
+ env,
175
+ step
176
+ });
@@ -0,0 +1,154 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/examples/env.ts
8
+ var env_exports = {};
9
+ __export(env_exports, {
10
+ push: () => push,
11
+ simulation: () => simulation
12
+ });
13
+ var mockSendServer = async () => {
14
+ return {
15
+ ok: true,
16
+ data: { request_id: "mock-request-id" }
17
+ };
18
+ };
19
+ var push = {
20
+ sendServer: mockSendServer
21
+ };
22
+ var simulation = ["sendServer"];
23
+
24
+ // src/examples/step.ts
25
+ var step_exports = {};
26
+ __export(step_exports, {
27
+ lead: () => lead,
28
+ purchase: () => purchase,
29
+ purchaseWithTwclid: () => purchaseWithTwclid
30
+ });
31
+ import { getEvent } from "@walkeros/core";
32
+ var ENDPOINT = "https://ads-api.x.com/12/measurement/conversions/o8z6j";
33
+ var HEADERS = {
34
+ headers: {
35
+ Authorization: "<OAUTH_SIGNATURE>",
36
+ "Content-Type": "application/json"
37
+ }
38
+ };
39
+ var purchase = {
40
+ in: getEvent("order complete", {
41
+ timestamp: 17000009e5,
42
+ data: { total: 249.99, currency: "EUR" },
43
+ user: { email: "jane@example.com" },
44
+ source: { type: "server", id: "https://shop.example.com", previous_id: "" }
45
+ }),
46
+ mapping: {
47
+ settings: {
48
+ value: "data.total",
49
+ currency: { key: "data.currency", value: "EUR" }
50
+ }
51
+ },
52
+ out: [
53
+ [
54
+ "sendServer",
55
+ ENDPOINT,
56
+ JSON.stringify({
57
+ conversions: [
58
+ {
59
+ conversion_time: "2023-11-14T22:28:20.000Z",
60
+ event_id: "tw-o8z6j-o8z21",
61
+ identifiers: [
62
+ {
63
+ hashed_email: "8c87b489ce35cf2e2f39f80e282cb2e804932a56a213983eeeb428407d43b52d"
64
+ }
65
+ ],
66
+ conversion_id: "1700000900000-gr0up-1",
67
+ value: "249.99"
68
+ }
69
+ ]
70
+ }),
71
+ HEADERS
72
+ ]
73
+ ]
74
+ };
75
+ var lead = {
76
+ in: getEvent("form submit", {
77
+ timestamp: 1700000901e3,
78
+ user: { email: "user@example.com" },
79
+ source: { type: "server", id: "https://example.com", previous_id: "" }
80
+ }),
81
+ mapping: void 0,
82
+ out: [
83
+ [
84
+ "sendServer",
85
+ ENDPOINT,
86
+ JSON.stringify({
87
+ conversions: [
88
+ {
89
+ conversion_time: "2023-11-14T22:28:21.000Z",
90
+ event_id: "tw-o8z6j-o8z21",
91
+ identifiers: [
92
+ {
93
+ hashed_email: "b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514"
94
+ }
95
+ ],
96
+ conversion_id: "1700000901000-gr0up-1"
97
+ }
98
+ ]
99
+ }),
100
+ HEADERS
101
+ ]
102
+ ]
103
+ };
104
+ var purchaseWithTwclid = {
105
+ in: getEvent("order complete", {
106
+ timestamp: 1700000902e3,
107
+ data: { total: 89.99, currency: "USD" },
108
+ user: { email: "buyer@co.com" },
109
+ context: { twclid: ["23opevjt88psuo13lu8d020qkn", 0] },
110
+ source: { type: "server", id: "https://shop.example.com", previous_id: "" }
111
+ }),
112
+ mapping: {
113
+ settings: {
114
+ value: "data.total",
115
+ currency: { key: "data.currency", value: "USD" }
116
+ },
117
+ data: {
118
+ map: {
119
+ user_data: {
120
+ map: {
121
+ twclid: "context.twclid"
122
+ }
123
+ }
124
+ }
125
+ }
126
+ },
127
+ out: [
128
+ [
129
+ "sendServer",
130
+ ENDPOINT,
131
+ JSON.stringify({
132
+ conversions: [
133
+ {
134
+ conversion_time: "2023-11-14T22:28:22.000Z",
135
+ event_id: "tw-o8z6j-o8z21",
136
+ identifiers: [
137
+ {
138
+ hashed_email: "484c39bfb51212665d9673805c112b5ba04cbf0460b6d3f00bcdc18b92afed66"
139
+ },
140
+ { twclid: "23opevjt88psuo13lu8d020qkn" }
141
+ ],
142
+ conversion_id: "1700000902000-gr0up-1",
143
+ value: "89.99"
144
+ }
145
+ ]
146
+ }),
147
+ HEADERS
148
+ ]
149
+ ]
150
+ };
151
+ export {
152
+ env_exports as env,
153
+ step_exports as step
154
+ };
@@ -0,0 +1,104 @@
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
+ pixelId: string;
6
+ eventId: string;
7
+ consumerKey: string;
8
+ consumerSecret: string;
9
+ accessToken: string;
10
+ accessTokenSecret: string;
11
+ apiVersion?: string;
12
+ doNotHash?: string[];
13
+ url?: string;
14
+ user_data?: Mapping$1.Map;
15
+ }
16
+ type InitSettings = Partial<Settings>;
17
+ interface Mapping {
18
+ eventId?: Mapping$1.Value;
19
+ value?: Mapping$1.Value;
20
+ currency?: Mapping$1.Value;
21
+ number_items?: Mapping$1.Value;
22
+ description?: Mapping$1.Value;
23
+ }
24
+ interface Env extends DestinationServer.Env {
25
+ sendServer?: typeof sendServer;
26
+ }
27
+ type Types = Destination$1.Types<Settings, Mapping, Env, InitSettings>;
28
+ interface Destination extends DestinationServer.Destination<Types> {
29
+ init: DestinationServer.InitFn<Types>;
30
+ }
31
+ type Config = {
32
+ settings: Settings;
33
+ } & DestinationServer.Config<Types>;
34
+ type InitFn = DestinationServer.InitFn<Types>;
35
+ type PushFn = DestinationServer.PushFn<Types>;
36
+ type PartialConfig = DestinationServer.PartialConfig<Types>;
37
+ type PushEvents = DestinationServer.PushEvents<Mapping>;
38
+ type Rule = Mapping$1.Rule<Mapping>;
39
+ type Rules = Mapping$1.Rules<Rule>;
40
+ /**
41
+ * User identifier -- each is a single-key object.
42
+ * X requires at least one primary identifier (twclid, hashed_email, hashed_phone_number).
43
+ * ip_address and user_agent are secondary identifiers.
44
+ */
45
+ type Identifier = {
46
+ twclid: string;
47
+ } | {
48
+ hashed_email: string;
49
+ } | {
50
+ hashed_phone_number: string;
51
+ } | {
52
+ ip_address: string;
53
+ } | {
54
+ user_agent: string;
55
+ };
56
+ /** Product/content detail within a conversion */
57
+ interface Content {
58
+ content_id?: string;
59
+ content_name?: string;
60
+ content_type?: string;
61
+ content_price?: number;
62
+ num_items?: number;
63
+ content_group_id?: string;
64
+ }
65
+ /** A single conversion event in the payload */
66
+ interface ConversionEvent {
67
+ conversion_time: string;
68
+ event_id: string;
69
+ identifiers: Identifier[];
70
+ conversion_id?: string;
71
+ value?: string;
72
+ number_items?: number;
73
+ description?: string;
74
+ contents?: Content[];
75
+ }
76
+ /** Top-level request body for X Conversions API */
77
+ interface ConversionsRequest {
78
+ conversions: ConversionEvent[];
79
+ }
80
+
81
+ type index_Config = Config;
82
+ type index_Content = Content;
83
+ type index_ConversionEvent = ConversionEvent;
84
+ type index_ConversionsRequest = ConversionsRequest;
85
+ type index_Destination = Destination;
86
+ type index_Env = Env;
87
+ type index_Identifier = Identifier;
88
+ type index_InitFn = InitFn;
89
+ type index_InitSettings = InitSettings;
90
+ type index_Mapping = Mapping;
91
+ type index_PartialConfig = PartialConfig;
92
+ type index_PushEvents = PushEvents;
93
+ type index_PushFn = PushFn;
94
+ type index_Rule = Rule;
95
+ type index_Rules = Rules;
96
+ type index_Settings = Settings;
97
+ type index_Types = Types;
98
+ declare namespace index {
99
+ export type { index_Config as Config, index_Content as Content, index_ConversionEvent as ConversionEvent, index_ConversionsRequest as ConversionsRequest, index_Destination as Destination, index_Env as Env, index_Identifier as Identifier, index_InitFn as InitFn, index_InitSettings as InitSettings, index_Mapping as Mapping, index_PartialConfig as PartialConfig, index_PushEvents as PushEvents, index_PushFn as PushFn, index_Rule as Rule, index_Rules as Rules, index_Settings as Settings, index_Types as Types };
100
+ }
101
+
102
+ declare const destinationTwitter: Destination;
103
+
104
+ export { index as DestinationTwitter, destinationTwitter as default, destinationTwitter };
@@ -0,0 +1,104 @@
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
+ pixelId: string;
6
+ eventId: string;
7
+ consumerKey: string;
8
+ consumerSecret: string;
9
+ accessToken: string;
10
+ accessTokenSecret: string;
11
+ apiVersion?: string;
12
+ doNotHash?: string[];
13
+ url?: string;
14
+ user_data?: Mapping$1.Map;
15
+ }
16
+ type InitSettings = Partial<Settings>;
17
+ interface Mapping {
18
+ eventId?: Mapping$1.Value;
19
+ value?: Mapping$1.Value;
20
+ currency?: Mapping$1.Value;
21
+ number_items?: Mapping$1.Value;
22
+ description?: Mapping$1.Value;
23
+ }
24
+ interface Env extends DestinationServer.Env {
25
+ sendServer?: typeof sendServer;
26
+ }
27
+ type Types = Destination$1.Types<Settings, Mapping, Env, InitSettings>;
28
+ interface Destination extends DestinationServer.Destination<Types> {
29
+ init: DestinationServer.InitFn<Types>;
30
+ }
31
+ type Config = {
32
+ settings: Settings;
33
+ } & DestinationServer.Config<Types>;
34
+ type InitFn = DestinationServer.InitFn<Types>;
35
+ type PushFn = DestinationServer.PushFn<Types>;
36
+ type PartialConfig = DestinationServer.PartialConfig<Types>;
37
+ type PushEvents = DestinationServer.PushEvents<Mapping>;
38
+ type Rule = Mapping$1.Rule<Mapping>;
39
+ type Rules = Mapping$1.Rules<Rule>;
40
+ /**
41
+ * User identifier -- each is a single-key object.
42
+ * X requires at least one primary identifier (twclid, hashed_email, hashed_phone_number).
43
+ * ip_address and user_agent are secondary identifiers.
44
+ */
45
+ type Identifier = {
46
+ twclid: string;
47
+ } | {
48
+ hashed_email: string;
49
+ } | {
50
+ hashed_phone_number: string;
51
+ } | {
52
+ ip_address: string;
53
+ } | {
54
+ user_agent: string;
55
+ };
56
+ /** Product/content detail within a conversion */
57
+ interface Content {
58
+ content_id?: string;
59
+ content_name?: string;
60
+ content_type?: string;
61
+ content_price?: number;
62
+ num_items?: number;
63
+ content_group_id?: string;
64
+ }
65
+ /** A single conversion event in the payload */
66
+ interface ConversionEvent {
67
+ conversion_time: string;
68
+ event_id: string;
69
+ identifiers: Identifier[];
70
+ conversion_id?: string;
71
+ value?: string;
72
+ number_items?: number;
73
+ description?: string;
74
+ contents?: Content[];
75
+ }
76
+ /** Top-level request body for X Conversions API */
77
+ interface ConversionsRequest {
78
+ conversions: ConversionEvent[];
79
+ }
80
+
81
+ type index_Config = Config;
82
+ type index_Content = Content;
83
+ type index_ConversionEvent = ConversionEvent;
84
+ type index_ConversionsRequest = ConversionsRequest;
85
+ type index_Destination = Destination;
86
+ type index_Env = Env;
87
+ type index_Identifier = Identifier;
88
+ type index_InitFn = InitFn;
89
+ type index_InitSettings = InitSettings;
90
+ type index_Mapping = Mapping;
91
+ type index_PartialConfig = PartialConfig;
92
+ type index_PushEvents = PushEvents;
93
+ type index_PushFn = PushFn;
94
+ type index_Rule = Rule;
95
+ type index_Rules = Rules;
96
+ type index_Settings = Settings;
97
+ type index_Types = Types;
98
+ declare namespace index {
99
+ export type { index_Config as Config, index_Content as Content, index_ConversionEvent as ConversionEvent, index_ConversionsRequest as ConversionsRequest, index_Destination as Destination, index_Env as Env, index_Identifier as Identifier, index_InitFn as InitFn, index_InitSettings as InitSettings, index_Mapping as Mapping, index_PartialConfig as PartialConfig, index_PushEvents as PushEvents, index_PushFn as PushFn, index_Rule as Rule, index_Rules as Rules, index_Settings as Settings, index_Types as Types };
100
+ }
101
+
102
+ declare const destinationTwitter: Destination;
103
+
104
+ export { index as DestinationTwitter, destinationTwitter as default, destinationTwitter };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";var mod,__create=Object.create,__defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__getProtoOf=Object.getPrototypeOf,__hasOwnProp=Object.prototype.hasOwnProperty,__copyProps=(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},__toESM=(mod,isNodeMode,target)=>(target=null!=mod?__create(__getProtoOf(mod)):{},__copyProps(!isNodeMode&&mod&&mod.__esModule?target:__defProp(target,"default",{value:mod,enumerable:!0}),mod)),index_exports={};((target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})})(index_exports,{DestinationTwitter:()=>types_exports,default:()=>index_default,destinationTwitter:()=>destinationTwitter}),module.exports=(mod=index_exports,__copyProps(__defProp({},"__esModule",{value:!0}),mod));var import_core=require("@walkeros/core"),import_server_core=require("@walkeros/server-core"),import_oauth_1=__toESM(require("oauth-1.0a")),import_crypto=__toESM(require("crypto"));function normalizeString(value){return(0,import_core.isString)(value)&&value.length>0?value:(0,import_core.isArray)(value)&&(0,import_core.isString)(value[0])&&value[0].length>0?value[0]:void 0}var types_exports={},destinationTwitter={type:"twitter",config:{},async init({config:partialConfig,logger:logger}){const config=function(partialConfig={},logger){const settings=partialConfig.settings||{},{pixelId:pixelId,eventId:eventId,consumerKey:consumerKey,consumerSecret:consumerSecret,accessToken:accessToken,accessTokenSecret:accessTokenSecret}=settings;pixelId||logger.throw("Config settings pixelId missing"),eventId||logger.throw("Config settings eventId missing"),consumerKey||logger.throw("Config settings consumerKey missing"),consumerSecret||logger.throw("Config settings consumerSecret missing"),accessToken||logger.throw("Config settings accessToken missing"),accessTokenSecret||logger.throw("Config settings accessTokenSecret missing");const settingsConfig={...settings,pixelId:pixelId,eventId:eventId,consumerKey:consumerKey,consumerSecret:consumerSecret,accessToken:accessToken,accessTokenSecret:accessTokenSecret,apiVersion:settings.apiVersion||"12"};return{...partialConfig,settings:settingsConfig}}(partialConfig,logger);return config},push:async(event,context)=>await async function(event,{config:config,rule:rule,data:data,env:env,logger:logger}){const{pixelId:pixelId,eventId:defaultEventId,consumerKey:consumerKey,consumerSecret:consumerSecret,accessToken:accessToken,accessTokenSecret:accessTokenSecret,apiVersion:apiVersion="12",doNotHash:doNotHash,url:url="https://ads-api.x.com/",user_data:user_data}=config.settings,userDataCustom=user_data?await(0,import_core.getMappingValue)(event,{map:user_data}):{},eventData=(0,import_core.isObject)(data)?data:{},userData={...(0,import_core.isObject)(userDataCustom)?userDataCustom:{},...(0,import_core.isObject)(eventData.user_data)?eventData.user_data:{}},identifiers=[],emailRaw=(0,import_core.isString)(userData.email)?userData.email:(0,import_core.isString)(event.user.email)?event.user.email:void 0;if(emailRaw){const normalizedEmail=emailRaw.trim().toLowerCase(),idValue=(null==doNotHash?void 0:doNotHash.includes("email"))?normalizedEmail:await(0,import_server_core.getHashServer)(normalizedEmail);identifiers.push({hashed_email:idValue})}const phoneRaw=(0,import_core.isString)(userData.phone)?userData.phone:(0,import_core.isString)(event.user.phone)?event.user.phone:void 0;if(phoneRaw){const normalizedPhone=phoneRaw.trim(),idValue=(null==doNotHash?void 0:doNotHash.includes("phone"))?normalizedPhone:await(0,import_server_core.getHashServer)(normalizedPhone);identifiers.push({hashed_phone_number:idValue})}const twclid=normalizeString(userData.twclid);twclid&&identifiers.push({twclid:twclid});const ipAddress=normalizeString(userData.ip_address);ipAddress&&identifiers.push({ip_address:ipAddress});const userAgent=normalizeString(userData.user_agent);if(userAgent&&identifiers.push({user_agent:userAgent}),!identifiers.some(id=>"twclid"in id||"hashed_email"in id||"hashed_phone_number"in id))return;const mappingSettings=(null==rule?void 0:rule.settings)||{},eventIdResolved=mappingSettings.eventId?await(0,import_core.getMappingValue)(event,mappingSettings.eventId):void 0,valueResolved=void 0!==mappingSettings.value?await(0,import_core.getMappingValue)(event,mappingSettings.value):void 0,numberItemsResolved=void 0!==mappingSettings.number_items?await(0,import_core.getMappingValue)(event,mappingSettings.number_items):void 0,descriptionResolved=void 0!==mappingSettings.description?await(0,import_core.getMappingValue)(event,mappingSettings.description):void 0,resolvedEventId=(0,import_core.isString)(eventIdResolved)?eventIdResolved:defaultEventId,conversion={conversion_time:new Date(event.timestamp).toISOString(),event_id:resolvedEventId,identifiers:identifiers,conversion_id:event.id};null!=valueResolved&&(conversion.value=String(valueResolved)),(0,import_core.isNumber)(numberItemsResolved)&&(conversion.number_items=numberItemsResolved),(0,import_core.isString)(descriptionResolved)&&descriptionResolved.length>0&&(conversion.description=descriptionResolved);const body={conversions:[conversion]},endpoint=`${url}${apiVersion}/measurement/conversions/${pixelId}`,oauth=new import_oauth_1.default({consumer:{key:consumerKey,secret:consumerSecret},signature_method:"HMAC-SHA1",hash_function:(baseString,key)=>import_crypto.default.createHmac("sha1",key).update(baseString).digest("base64")}),authHeader=oauth.toHeader(oauth.authorize({url:endpoint,method:"POST"},{key:accessToken,secret:accessTokenSecret}));logger.debug("Calling X Conversions API",{endpoint:endpoint,method:"POST",event_id:resolvedEventId,conversion_id:event.id});const sendServerFn=(null==env?void 0:env.sendServer)||import_server_core.sendServer,result=await sendServerFn(endpoint,JSON.stringify(body),{headers:{Authorization:authHeader.Authorization,"Content-Type":"application/json"}});logger.debug("X Conversions API response",{ok:!(0,import_core.isObject)(result)||result.ok}),(0,import_core.isObject)(result)&&!1===result.ok&&logger.throw(`X Conversions API error: ${JSON.stringify(result)}`)}(event,context)},index_default=destinationTwitter;//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/push.ts","../src/types/index.ts"],"sourcesContent":["import type { Destination } from './types';\nimport { getConfig } from './config';\nimport { push } from './push';\n\n// Types\nexport * as DestinationTwitter from './types';\n\nexport const destinationTwitter: Destination = {\n type: 'twitter',\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 destinationTwitter;\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 {\n pixelId,\n eventId,\n consumerKey,\n consumerSecret,\n accessToken,\n accessTokenSecret,\n } = settings;\n\n if (!pixelId) logger.throw('Config settings pixelId missing');\n if (!eventId) logger.throw('Config settings eventId missing');\n if (!consumerKey) logger.throw('Config settings consumerKey missing');\n if (!consumerSecret) logger.throw('Config settings consumerSecret missing');\n if (!accessToken) logger.throw('Config settings accessToken missing');\n if (!accessTokenSecret)\n logger.throw('Config settings accessTokenSecret missing');\n\n const settingsConfig: Settings = {\n ...settings,\n pixelId,\n eventId,\n consumerKey,\n consumerSecret,\n accessToken,\n accessTokenSecret,\n apiVersion: settings.apiVersion || '12',\n };\n\n return { ...partialConfig, settings: settingsConfig };\n}\n","import type {\n ConversionEvent,\n ConversionsRequest,\n Env,\n Identifier,\n Mapping,\n PushFn,\n Settings,\n} from './types';\nimport {\n getMappingValue,\n isArray,\n isNumber,\n isObject,\n isString,\n} from '@walkeros/core';\nimport { sendServer, getHashServer } from '@walkeros/server-core';\nimport OAuth from 'oauth-1.0a';\nimport crypto from 'crypto';\n\nfunction normalizeString(value: unknown): string | undefined {\n if (isString(value) && value.length > 0) return value;\n if (isArray(value) && isString(value[0]) && value[0].length > 0)\n return value[0];\n return undefined;\n}\n\nexport const push: PushFn = async function (\n event,\n { config, rule, data, env, logger },\n) {\n const {\n pixelId,\n eventId: defaultEventId,\n consumerKey,\n consumerSecret,\n accessToken,\n accessTokenSecret,\n apiVersion = '12',\n doNotHash,\n url = 'https://ads-api.x.com/',\n user_data,\n } = config.settings as Settings;\n\n // Resolve user data from settings-level mapping\n const userDataCustom = user_data\n ? await getMappingValue(event, { map: user_data })\n : {};\n\n // Merge user data sources: config mapping + event mapping data\n const eventData = isObject(data) ? data : {};\n const userData: Record<string, unknown> = {\n ...(isObject(userDataCustom) ? userDataCustom : {}),\n ...(isObject(eventData.user_data) ? eventData.user_data : {}),\n };\n\n // Build identifiers array (each is a single-key object)\n const identifiers: Identifier[] = [];\n\n // hashed_email\n const emailRaw = isString(userData.email)\n ? userData.email\n : isString(event.user.email)\n ? event.user.email\n : undefined;\n if (emailRaw) {\n const normalizedEmail = emailRaw.trim().toLowerCase();\n const shouldHash = !doNotHash?.includes('email');\n const idValue = shouldHash\n ? await getHashServer(normalizedEmail)\n : normalizedEmail;\n identifiers.push({ hashed_email: idValue });\n }\n\n // hashed_phone_number\n const phoneRaw = isString(userData.phone)\n ? userData.phone\n : isString(event.user.phone)\n ? event.user.phone\n : undefined;\n if (phoneRaw) {\n const normalizedPhone = phoneRaw.trim();\n const shouldHash = !doNotHash?.includes('phone');\n const idValue = shouldHash\n ? await getHashServer(normalizedPhone)\n : normalizedPhone;\n identifiers.push({ hashed_phone_number: idValue });\n }\n\n // twclid (pass-through, handles context tuple [value, order])\n const twclid = normalizeString(userData.twclid);\n if (twclid) {\n identifiers.push({ twclid });\n }\n\n // ip_address (pass-through, secondary)\n const ipAddress = normalizeString(userData.ip_address);\n if (ipAddress) {\n identifiers.push({ ip_address: ipAddress });\n }\n\n // user_agent (pass-through, secondary)\n const userAgent = normalizeString(userData.user_agent);\n if (userAgent) {\n identifiers.push({ user_agent: userAgent });\n }\n\n // Skip event if no primary identifier is present\n const hasPrimary = identifiers.some(\n (id) =>\n 'twclid' in id || 'hashed_email' in id || 'hashed_phone_number' in id,\n );\n if (!hasPrimary) return;\n\n // Resolve per-event mapping settings\n const mappingSettings = (rule?.settings || {}) as Mapping;\n const eventIdResolved = mappingSettings.eventId\n ? await getMappingValue(event, mappingSettings.eventId)\n : undefined;\n const valueResolved =\n mappingSettings.value !== undefined\n ? await getMappingValue(event, mappingSettings.value)\n : undefined;\n const numberItemsResolved =\n mappingSettings.number_items !== undefined\n ? await getMappingValue(event, mappingSettings.number_items)\n : undefined;\n const descriptionResolved =\n mappingSettings.description !== undefined\n ? await getMappingValue(event, mappingSettings.description)\n : undefined;\n\n const resolvedEventId = isString(eventIdResolved)\n ? eventIdResolved\n : defaultEventId;\n\n // Construct conversion\n const conversion: ConversionEvent = {\n conversion_time: new Date(event.timestamp).toISOString(),\n event_id: resolvedEventId,\n identifiers,\n conversion_id: event.id,\n };\n\n if (valueResolved !== undefined && valueResolved !== null) {\n conversion.value = String(valueResolved);\n }\n\n if (isNumber(numberItemsResolved)) {\n conversion.number_items = numberItemsResolved;\n }\n\n if (isString(descriptionResolved) && descriptionResolved.length > 0) {\n conversion.description = descriptionResolved;\n }\n\n const body: ConversionsRequest = {\n conversions: [conversion],\n };\n\n const endpoint = `${url}${apiVersion}/measurement/conversions/${pixelId}`;\n\n // Generate OAuth 1.0a header (stateless, safe to create per-request)\n const oauth = new OAuth({\n consumer: { key: consumerKey, secret: consumerSecret },\n signature_method: 'HMAC-SHA1',\n hash_function(baseString: string, key: string) {\n return crypto.createHmac('sha1', key).update(baseString).digest('base64');\n },\n });\n\n const authHeader = oauth.toHeader(\n oauth.authorize(\n { url: endpoint, method: 'POST' },\n { key: accessToken, secret: accessTokenSecret },\n ),\n );\n\n logger.debug('Calling X Conversions API', {\n endpoint,\n method: 'POST',\n event_id: resolvedEventId,\n conversion_id: event.id,\n });\n\n const sendServerFn = (env as Env)?.sendServer || sendServer;\n const result = await sendServerFn(endpoint, JSON.stringify(body), {\n headers: {\n Authorization: authHeader.Authorization,\n 'Content-Type': 'application/json',\n },\n });\n\n logger.debug('X Conversions API response', {\n ok: isObject(result) ? result.ok : true,\n });\n\n if (isObject(result) && result.ok === false) {\n logger.throw(`X Conversions API error: ${JSON.stringify(result)}`);\n }\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 pixelId: string;\n eventId: string;\n consumerKey: string;\n consumerSecret: string;\n accessToken: string;\n accessTokenSecret: string;\n apiVersion?: string;\n doNotHash?: string[];\n url?: string;\n user_data?: WalkerOSMapping.Map;\n}\n\nexport type InitSettings = Partial<Settings>;\n\nexport interface Mapping {\n eventId?: WalkerOSMapping.Value;\n value?: WalkerOSMapping.Value;\n currency?: WalkerOSMapping.Value;\n number_items?: WalkerOSMapping.Value;\n description?: WalkerOSMapping.Value;\n}\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// X (Twitter) Conversions API types\n\n/**\n * User identifier -- each is a single-key object.\n * X requires at least one primary identifier (twclid, hashed_email, hashed_phone_number).\n * ip_address and user_agent are secondary identifiers.\n */\nexport type Identifier =\n | { twclid: string }\n | { hashed_email: string }\n | { hashed_phone_number: string }\n | { ip_address: string }\n | { user_agent: string };\n\n/** Product/content detail within a conversion */\nexport interface Content {\n content_id?: string;\n content_name?: string;\n content_type?: string;\n content_price?: number;\n num_items?: number;\n content_group_id?: string;\n}\n\n/** A single conversion event in the payload */\nexport interface ConversionEvent {\n conversion_time: string;\n event_id: string;\n identifiers: Identifier[];\n conversion_id?: string;\n value?: string;\n number_items?: number;\n description?: string;\n contents?: Content[];\n}\n\n/** Top-level request body for X Conversions API */\nexport interface ConversionsRequest {\n conversions: ConversionEvent[];\n}\n"],"mappings":"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","names":["OAuth","crypto"]}
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ import{getMappingValue,isArray,isNumber,isObject,isString}from"@walkeros/core";import{sendServer,getHashServer}from"@walkeros/server-core";import OAuth from"oauth-1.0a";import crypto from"crypto";function normalizeString(value){return isString(value)&&value.length>0?value:isArray(value)&&isString(value[0])&&value[0].length>0?value[0]:void 0}var types_exports={},destinationTwitter={type:"twitter",config:{},async init({config:partialConfig,logger:logger}){const config=function(partialConfig={},logger){const settings=partialConfig.settings||{},{pixelId:pixelId,eventId:eventId,consumerKey:consumerKey,consumerSecret:consumerSecret,accessToken:accessToken,accessTokenSecret:accessTokenSecret}=settings;pixelId||logger.throw("Config settings pixelId missing"),eventId||logger.throw("Config settings eventId missing"),consumerKey||logger.throw("Config settings consumerKey missing"),consumerSecret||logger.throw("Config settings consumerSecret missing"),accessToken||logger.throw("Config settings accessToken missing"),accessTokenSecret||logger.throw("Config settings accessTokenSecret missing");const settingsConfig={...settings,pixelId:pixelId,eventId:eventId,consumerKey:consumerKey,consumerSecret:consumerSecret,accessToken:accessToken,accessTokenSecret:accessTokenSecret,apiVersion:settings.apiVersion||"12"};return{...partialConfig,settings:settingsConfig}}(partialConfig,logger);return config},push:async(event,context)=>await async function(event,{config:config,rule:rule,data:data,env:env,logger:logger}){const{pixelId:pixelId,eventId:defaultEventId,consumerKey:consumerKey,consumerSecret:consumerSecret,accessToken:accessToken,accessTokenSecret:accessTokenSecret,apiVersion:apiVersion="12",doNotHash:doNotHash,url:url="https://ads-api.x.com/",user_data:user_data}=config.settings,userDataCustom=user_data?await getMappingValue(event,{map:user_data}):{},eventData=isObject(data)?data:{},userData={...isObject(userDataCustom)?userDataCustom:{},...isObject(eventData.user_data)?eventData.user_data:{}},identifiers=[],emailRaw=isString(userData.email)?userData.email:isString(event.user.email)?event.user.email:void 0;if(emailRaw){const normalizedEmail=emailRaw.trim().toLowerCase(),idValue=(null==doNotHash?void 0:doNotHash.includes("email"))?normalizedEmail:await getHashServer(normalizedEmail);identifiers.push({hashed_email:idValue})}const phoneRaw=isString(userData.phone)?userData.phone:isString(event.user.phone)?event.user.phone:void 0;if(phoneRaw){const normalizedPhone=phoneRaw.trim(),idValue=(null==doNotHash?void 0:doNotHash.includes("phone"))?normalizedPhone:await getHashServer(normalizedPhone);identifiers.push({hashed_phone_number:idValue})}const twclid=normalizeString(userData.twclid);twclid&&identifiers.push({twclid:twclid});const ipAddress=normalizeString(userData.ip_address);ipAddress&&identifiers.push({ip_address:ipAddress});const userAgent=normalizeString(userData.user_agent);if(userAgent&&identifiers.push({user_agent:userAgent}),!identifiers.some(id=>"twclid"in id||"hashed_email"in id||"hashed_phone_number"in id))return;const mappingSettings=(null==rule?void 0:rule.settings)||{},eventIdResolved=mappingSettings.eventId?await getMappingValue(event,mappingSettings.eventId):void 0,valueResolved=void 0!==mappingSettings.value?await getMappingValue(event,mappingSettings.value):void 0,numberItemsResolved=void 0!==mappingSettings.number_items?await getMappingValue(event,mappingSettings.number_items):void 0,descriptionResolved=void 0!==mappingSettings.description?await getMappingValue(event,mappingSettings.description):void 0,resolvedEventId=isString(eventIdResolved)?eventIdResolved:defaultEventId,conversion={conversion_time:new Date(event.timestamp).toISOString(),event_id:resolvedEventId,identifiers:identifiers,conversion_id:event.id};null!=valueResolved&&(conversion.value=String(valueResolved)),isNumber(numberItemsResolved)&&(conversion.number_items=numberItemsResolved),isString(descriptionResolved)&&descriptionResolved.length>0&&(conversion.description=descriptionResolved);const body={conversions:[conversion]},endpoint=`${url}${apiVersion}/measurement/conversions/${pixelId}`,oauth=new OAuth({consumer:{key:consumerKey,secret:consumerSecret},signature_method:"HMAC-SHA1",hash_function:(baseString,key)=>crypto.createHmac("sha1",key).update(baseString).digest("base64")}),authHeader=oauth.toHeader(oauth.authorize({url:endpoint,method:"POST"},{key:accessToken,secret:accessTokenSecret}));logger.debug("Calling X Conversions API",{endpoint:endpoint,method:"POST",event_id:resolvedEventId,conversion_id:event.id});const sendServerFn=(null==env?void 0:env.sendServer)||sendServer,result=await sendServerFn(endpoint,JSON.stringify(body),{headers:{Authorization:authHeader.Authorization,"Content-Type":"application/json"}});logger.debug("X Conversions API response",{ok:!isObject(result)||result.ok}),isObject(result)&&!1===result.ok&&logger.throw(`X Conversions API error: ${JSON.stringify(result)}`)}(event,context)},index_default=destinationTwitter;export{types_exports as DestinationTwitter,index_default as default,destinationTwitter};//# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts","../src/push.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 {\n pixelId,\n eventId,\n consumerKey,\n consumerSecret,\n accessToken,\n accessTokenSecret,\n } = settings;\n\n if (!pixelId) logger.throw('Config settings pixelId missing');\n if (!eventId) logger.throw('Config settings eventId missing');\n if (!consumerKey) logger.throw('Config settings consumerKey missing');\n if (!consumerSecret) logger.throw('Config settings consumerSecret missing');\n if (!accessToken) logger.throw('Config settings accessToken missing');\n if (!accessTokenSecret)\n logger.throw('Config settings accessTokenSecret missing');\n\n const settingsConfig: Settings = {\n ...settings,\n pixelId,\n eventId,\n consumerKey,\n consumerSecret,\n accessToken,\n accessTokenSecret,\n apiVersion: settings.apiVersion || '12',\n };\n\n return { ...partialConfig, settings: settingsConfig };\n}\n","import type {\n ConversionEvent,\n ConversionsRequest,\n Env,\n Identifier,\n Mapping,\n PushFn,\n Settings,\n} from './types';\nimport {\n getMappingValue,\n isArray,\n isNumber,\n isObject,\n isString,\n} from '@walkeros/core';\nimport { sendServer, getHashServer } from '@walkeros/server-core';\nimport OAuth from 'oauth-1.0a';\nimport crypto from 'crypto';\n\nfunction normalizeString(value: unknown): string | undefined {\n if (isString(value) && value.length > 0) return value;\n if (isArray(value) && isString(value[0]) && value[0].length > 0)\n return value[0];\n return undefined;\n}\n\nexport const push: PushFn = async function (\n event,\n { config, rule, data, env, logger },\n) {\n const {\n pixelId,\n eventId: defaultEventId,\n consumerKey,\n consumerSecret,\n accessToken,\n accessTokenSecret,\n apiVersion = '12',\n doNotHash,\n url = 'https://ads-api.x.com/',\n user_data,\n } = config.settings as Settings;\n\n // Resolve user data from settings-level mapping\n const userDataCustom = user_data\n ? await getMappingValue(event, { map: user_data })\n : {};\n\n // Merge user data sources: config mapping + event mapping data\n const eventData = isObject(data) ? data : {};\n const userData: Record<string, unknown> = {\n ...(isObject(userDataCustom) ? userDataCustom : {}),\n ...(isObject(eventData.user_data) ? eventData.user_data : {}),\n };\n\n // Build identifiers array (each is a single-key object)\n const identifiers: Identifier[] = [];\n\n // hashed_email\n const emailRaw = isString(userData.email)\n ? userData.email\n : isString(event.user.email)\n ? event.user.email\n : undefined;\n if (emailRaw) {\n const normalizedEmail = emailRaw.trim().toLowerCase();\n const shouldHash = !doNotHash?.includes('email');\n const idValue = shouldHash\n ? await getHashServer(normalizedEmail)\n : normalizedEmail;\n identifiers.push({ hashed_email: idValue });\n }\n\n // hashed_phone_number\n const phoneRaw = isString(userData.phone)\n ? userData.phone\n : isString(event.user.phone)\n ? event.user.phone\n : undefined;\n if (phoneRaw) {\n const normalizedPhone = phoneRaw.trim();\n const shouldHash = !doNotHash?.includes('phone');\n const idValue = shouldHash\n ? await getHashServer(normalizedPhone)\n : normalizedPhone;\n identifiers.push({ hashed_phone_number: idValue });\n }\n\n // twclid (pass-through, handles context tuple [value, order])\n const twclid = normalizeString(userData.twclid);\n if (twclid) {\n identifiers.push({ twclid });\n }\n\n // ip_address (pass-through, secondary)\n const ipAddress = normalizeString(userData.ip_address);\n if (ipAddress) {\n identifiers.push({ ip_address: ipAddress });\n }\n\n // user_agent (pass-through, secondary)\n const userAgent = normalizeString(userData.user_agent);\n if (userAgent) {\n identifiers.push({ user_agent: userAgent });\n }\n\n // Skip event if no primary identifier is present\n const hasPrimary = identifiers.some(\n (id) =>\n 'twclid' in id || 'hashed_email' in id || 'hashed_phone_number' in id,\n );\n if (!hasPrimary) return;\n\n // Resolve per-event mapping settings\n const mappingSettings = (rule?.settings || {}) as Mapping;\n const eventIdResolved = mappingSettings.eventId\n ? await getMappingValue(event, mappingSettings.eventId)\n : undefined;\n const valueResolved =\n mappingSettings.value !== undefined\n ? await getMappingValue(event, mappingSettings.value)\n : undefined;\n const numberItemsResolved =\n mappingSettings.number_items !== undefined\n ? await getMappingValue(event, mappingSettings.number_items)\n : undefined;\n const descriptionResolved =\n mappingSettings.description !== undefined\n ? await getMappingValue(event, mappingSettings.description)\n : undefined;\n\n const resolvedEventId = isString(eventIdResolved)\n ? eventIdResolved\n : defaultEventId;\n\n // Construct conversion\n const conversion: ConversionEvent = {\n conversion_time: new Date(event.timestamp).toISOString(),\n event_id: resolvedEventId,\n identifiers,\n conversion_id: event.id,\n };\n\n if (valueResolved !== undefined && valueResolved !== null) {\n conversion.value = String(valueResolved);\n }\n\n if (isNumber(numberItemsResolved)) {\n conversion.number_items = numberItemsResolved;\n }\n\n if (isString(descriptionResolved) && descriptionResolved.length > 0) {\n conversion.description = descriptionResolved;\n }\n\n const body: ConversionsRequest = {\n conversions: [conversion],\n };\n\n const endpoint = `${url}${apiVersion}/measurement/conversions/${pixelId}`;\n\n // Generate OAuth 1.0a header (stateless, safe to create per-request)\n const oauth = new OAuth({\n consumer: { key: consumerKey, secret: consumerSecret },\n signature_method: 'HMAC-SHA1',\n hash_function(baseString: string, key: string) {\n return crypto.createHmac('sha1', key).update(baseString).digest('base64');\n },\n });\n\n const authHeader = oauth.toHeader(\n oauth.authorize(\n { url: endpoint, method: 'POST' },\n { key: accessToken, secret: accessTokenSecret },\n ),\n );\n\n logger.debug('Calling X Conversions API', {\n endpoint,\n method: 'POST',\n event_id: resolvedEventId,\n conversion_id: event.id,\n });\n\n const sendServerFn = (env as Env)?.sendServer || sendServer;\n const result = await sendServerFn(endpoint, JSON.stringify(body), {\n headers: {\n Authorization: authHeader.Authorization,\n 'Content-Type': 'application/json',\n },\n });\n\n logger.debug('X Conversions API response', {\n ok: isObject(result) ? result.ok : true,\n });\n\n if (isObject(result) && result.ok === false) {\n logger.throw(`X Conversions API error: ${JSON.stringify(result)}`);\n }\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 pixelId: string;\n eventId: string;\n consumerKey: string;\n consumerSecret: string;\n accessToken: string;\n accessTokenSecret: string;\n apiVersion?: string;\n doNotHash?: string[];\n url?: string;\n user_data?: WalkerOSMapping.Map;\n}\n\nexport type InitSettings = Partial<Settings>;\n\nexport interface Mapping {\n eventId?: WalkerOSMapping.Value;\n value?: WalkerOSMapping.Value;\n currency?: WalkerOSMapping.Value;\n number_items?: WalkerOSMapping.Value;\n description?: WalkerOSMapping.Value;\n}\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// X (Twitter) Conversions API types\n\n/**\n * User identifier -- each is a single-key object.\n * X requires at least one primary identifier (twclid, hashed_email, hashed_phone_number).\n * ip_address and user_agent are secondary identifiers.\n */\nexport type Identifier =\n | { twclid: string }\n | { hashed_email: string }\n | { hashed_phone_number: string }\n | { ip_address: string }\n | { user_agent: string };\n\n/** Product/content detail within a conversion */\nexport interface Content {\n content_id?: string;\n content_name?: string;\n content_type?: string;\n content_price?: number;\n num_items?: number;\n content_group_id?: string;\n}\n\n/** A single conversion event in the payload */\nexport interface ConversionEvent {\n conversion_time: string;\n event_id: string;\n identifiers: Identifier[];\n conversion_id?: string;\n value?: string;\n number_items?: number;\n description?: string;\n contents?: Content[];\n}\n\n/** Top-level request body for X Conversions API */\nexport interface ConversionsRequest {\n conversions: ConversionEvent[];\n}\n","import type { Destination } from './types';\nimport { getConfig } from './config';\nimport { push } from './push';\n\n// Types\nexport * as DestinationTwitter from './types';\n\nexport const destinationTwitter: Destination = {\n type: 'twitter',\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 destinationTwitter;\n"],"mappings":"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","names":[]}