@walkeros/server-destination-twitter 3.4.1 → 4.0.0-next-1777463920154

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/dev.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e,t=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,s=Object.prototype.hasOwnProperty,o=(e,r)=>{for(var i in r)t(e,i,{get:r[i],enumerable:!0})},n={};o(n,{examples:()=>x,schemas:()=>a}),module.exports=(e=n,((e,o,n,a)=>{if(o&&"object"==typeof o||"function"==typeof o)for(let c of i(o))s.call(e,c)||c===n||t(e,c,{get:()=>o[c],enumerable:!(a=r(o,c))||a.enumerable});return e})(t({},"__esModule",{value:!0}),e));var a={};o(a,{ApiVersionSchema:()=>p,EventIdSchema:()=>m,IdentifierTypeSchema:()=>l,MappingSchema:()=>b,SettingsSchema:()=>v,mapping:()=>f,settings:()=>g});var c=require("@walkeros/core/dev"),d=require("@walkeros/core/dev"),u=require("@walkeros/core/dev"),l=u.z.enum(["twclid","hashed_email","hashed_phone_number","ip_address","user_agent"]),m=u.z.string().regex(/^tw-[a-z0-9]+-[a-z0-9]+$/,"Event ID must match X format (e.g. tw-xxxxx-xxxxx)"),p=u.z.string().regex(/^\d+$/,'API version must be numeric (e.g. "12")'),v=d.z.object({pixelId:d.z.string().min(1).describe("X Pixel ID used in the Conversions API endpoint URL"),eventId:m.describe("Default pre-registered conversion event ID (like tw-xxxxx-xxxxx)"),consumerKey:d.z.string().min(1).describe("OAuth 1.0a API Key (Consumer Key) for X Ads API"),consumerSecret:d.z.string().min(1).describe("OAuth 1.0a API Key Secret (Consumer Secret)"),accessToken:d.z.string().min(1).describe("OAuth 1.0a User Access Token"),accessTokenSecret:d.z.string().min(1).describe("OAuth 1.0a User Access Token Secret"),apiVersion:p.describe('X Ads API version number (like "12")').optional(),doNotHash:d.z.array(d.z.string()).describe("Array of user data fields that should not be hashed (like ['email'])").optional(),url:d.z.string().url().describe("Custom base URL for the X Conversions API endpoint (like https://ads-api.x.com/)").optional(),user_data:d.z.record(d.z.string(),d.z.string()).describe("Mapping configuration for user identifiers (like { email: 'user.email', twclid: 'context.twclid' })").optional()}),h=require("@walkeros/core/dev"),b=h.z.object({eventId:h.z.string().describe("Override the default conversion event ID for this event").optional(),value:h.z.union([h.z.string(),h.z.number()]).describe("Conversion monetary value (sent to X as a string)").optional(),currency:h.z.string().describe("ISO 4217 currency code (like USD, EUR)").optional(),number_items:h.z.number().int().describe("Number of items in the conversion").optional(),description:h.z.string().describe("Free-text description of the conversion").optional()}),g=(0,c.zodToSchema)(v),f=(0,c.zodToSchema)(b),x={};o(x,{env:()=>z,step:()=>S});var z={};o(z,{push:()=>y,simulation:()=>w});var y={sendServer:async()=>({ok:!0,data:{request_id:"mock-request-id"}})},w=["sendServer"],S={};o(S,{lead:()=>O,purchase:()=>I,purchaseWithTwclid:()=>j});var _=require("@walkeros/core"),A="https://ads-api.x.com/12/measurement/conversions/o8z6j",k={headers:{Authorization:"<OAUTH_SIGNATURE>","Content-Type":"application/json"}},I={title:"Purchase",description:"A completed order is sent to the X (Twitter) Conversions API with conversion value and the hashed email.",in:(0,_.getEvent)("order complete",{timestamp:17000009e5,data:{total:249.99,currency:"EUR"},user:{email:"jane@example.com"},source:{type:"server",id:"https://shop.example.com",previous_id:""}}),mapping:{settings:{value:"data.total",currency:{key:"data.currency",value:"EUR"}}},out:[["sendServer",A,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:20.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"8c87b489ce35cf2e2f39f80e282cb2e804932a56a213983eeeb428407d43b52d"}],conversion_id:"1700000900000-gr0up-1",value:"249.99"}]}),k]]},O={title:"Lead",description:"A form submission is sent to X as a conversion with the SHA-256 hashed email as the identifier.",in:(0,_.getEvent)("form submit",{timestamp:1700000901e3,user:{email:"user@example.com"},source:{type:"server",id:"https://example.com",previous_id:""}}),mapping:void 0,out:[["sendServer",A,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:21.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514"}],conversion_id:"1700000901000-gr0up-1"}]}),k]]},j={title:"Purchase with twclid",description:"A purchase is sent to X with both the hashed email and the twclid click id for ads attribution.",in:(0,_.getEvent)("order complete",{timestamp:1700000902e3,data:{total:89.99,currency:"USD"},user:{email:"buyer@co.com"},context:{twclid:["23opevjt88psuo13lu8d020qkn",0]},source:{type:"server",id:"https://shop.example.com",previous_id:""}}),mapping:{settings:{value:"data.total",currency:{key:"data.currency",value:"USD"}},data:{map:{user_data:{map:{twclid:"context.twclid"}}}}},out:[["sendServer",A,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:22.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"484c39bfb51212665d9673805c112b5ba04cbf0460b6d3f00bcdc18b92afed66"},{twclid:"23opevjt88psuo13lu8d020qkn"}],conversion_id:"1700000902000-gr0up-1",value:"89.99"}]}),k]]};//# sourceMappingURL=dev.js.map
1
+ "use strict";var e,t=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,s=Object.prototype.hasOwnProperty,n=(e,r)=>{for(var i in r)t(e,i,{get:r[i],enumerable:!0})},o={};n(o,{examples:()=>x,schemas:()=>a}),module.exports=(e=o,((e,n,o,a)=>{if(n&&"object"==typeof n||"function"==typeof n)for(let c of i(n))s.call(e,c)||c===o||t(e,c,{get:()=>n[c],enumerable:!(a=r(n,c))||a.enumerable});return e})(t({},"__esModule",{value:!0}),e));var a={};n(a,{ApiVersionSchema:()=>p,EventIdSchema:()=>m,IdentifierTypeSchema:()=>l,MappingSchema:()=>b,SettingsSchema:()=>v,mapping:()=>g,settings:()=>f});var c=require("@walkeros/core/dev"),d=require("@walkeros/core/dev"),u=require("@walkeros/core/dev"),l=u.z.enum(["twclid","hashed_email","hashed_phone_number","ip_address","user_agent"]),m=u.z.string().regex(/^tw-[a-z0-9]+-[a-z0-9]+$/,"Event ID must match X format (e.g. tw-xxxxx-xxxxx)"),p=u.z.string().regex(/^\d+$/,'API version must be numeric (e.g. "12")'),v=d.z.object({pixelId:d.z.string().min(1).describe("X Pixel ID used in the Conversions API endpoint URL"),eventId:m.describe("Default pre-registered conversion event ID (like tw-xxxxx-xxxxx)"),consumerKey:d.z.string().min(1).describe("OAuth 1.0a API Key (Consumer Key) for X Ads API"),consumerSecret:d.z.string().min(1).describe("OAuth 1.0a API Key Secret (Consumer Secret)"),accessToken:d.z.string().min(1).describe("OAuth 1.0a User Access Token"),accessTokenSecret:d.z.string().min(1).describe("OAuth 1.0a User Access Token Secret"),apiVersion:p.describe('X Ads API version number (like "12")').optional(),doNotHash:d.z.array(d.z.string()).describe("Array of user data fields that should not be hashed (like ['email'])").optional(),url:d.z.string().url().describe("Custom base URL for the X Conversions API endpoint (like https://ads-api.x.com/)").optional(),user_data:d.z.record(d.z.string(),d.z.string()).describe("Mapping configuration for user identifiers (like { email: 'user.email', twclid: 'context.twclid' })").optional()}),h=require("@walkeros/core/dev"),b=h.z.object({eventId:h.z.string().describe("Override the default conversion event ID for this event").optional(),value:h.z.union([h.z.string(),h.z.number()]).describe("Conversion monetary value (sent to X as a string)").optional(),currency:h.z.string().describe("ISO 4217 currency code (like USD, EUR)").optional(),number_items:h.z.number().int().describe("Number of items in the conversion").optional(),description:h.z.string().describe("Free-text description of the conversion").optional()}),f=(0,c.zodToSchema)(v),g=(0,c.zodToSchema)(b),x={};n(x,{env:()=>z,step:()=>S});var z={};n(z,{push:()=>y,simulation:()=>w});var y={sendServer:async()=>({ok:!0,data:{request_id:"mock-request-id"}})},w=["sendServer"],S={};n(S,{lead:()=>O,purchase:()=>I,purchaseWithTwclid:()=>j});var A=require("@walkeros/core"),_="https://ads-api.x.com/12/measurement/conversions/o8z6j",k={headers:{Authorization:"<OAUTH_SIGNATURE>","Content-Type":"application/json"}},I={title:"Purchase",description:"A completed order is sent to the X (Twitter) Conversions API with conversion value and the hashed email.",in:(0,A.getEvent)("order complete",{id:"ev-1700000900000",timestamp:17000009e5,data:{total:249.99,currency:"EUR"},user:{email:"jane@example.com"},source:{type:"express",platform:"server"}}),mapping:{settings:{value:"data.total",currency:{key:"data.currency",value:"EUR"}}},out:[["sendServer",_,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:20.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"8c87b489ce35cf2e2f39f80e282cb2e804932a56a213983eeeb428407d43b52d"}],conversion_id:"ev-1700000900000",value:"249.99"}]}),k]]},O={title:"Lead",description:"A form submission is sent to X as a conversion with the SHA-256 hashed email as the identifier.",in:(0,A.getEvent)("form submit",{id:"ev-1700000901000",timestamp:1700000901e3,user:{email:"user@example.com"},source:{type:"express",platform:"server"}}),mapping:void 0,out:[["sendServer",_,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:21.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514"}],conversion_id:"ev-1700000901000"}]}),k]]},j={title:"Purchase with twclid",description:"A purchase is sent to X with both the hashed email and the twclid click id for ads attribution.",in:(0,A.getEvent)("order complete",{id:"ev-1700000902000",timestamp:1700000902e3,data:{total:89.99,currency:"USD"},user:{email:"buyer@co.com"},context:{twclid:["23opevjt88psuo13lu8d020qkn",0]},source:{type:"express",platform:"server"}}),mapping:{settings:{value:"data.total",currency:{key:"data.currency",value:"USD"}},data:{map:{user_data:{map:{twclid:"context.twclid"}}}}},out:[["sendServer",_,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:22.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"484c39bfb51212665d9673805c112b5ba04cbf0460b6d3f00bcdc18b92afed66"},{twclid:"23opevjt88psuo13lu8d020qkn"}],conversion_id:"ev-1700000902000",value:"89.99"}]}),k]]};//# sourceMappingURL=dev.js.map
package/dist/dev.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/dev.ts","../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/primitives.ts","../src/schemas/mapping.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts"],"sourcesContent":["export * as schemas from './schemas';\nexport * as examples from './examples';\n","import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { MappingSchema } from './mapping';\n\nexport * from './primitives';\n\nexport { SettingsSchema, type Settings } from './settings';\nexport { MappingSchema, type Mapping } from './mapping';\n\n// JSON Schema\nexport const settings = zodToSchema(SettingsSchema);\nexport const mapping = zodToSchema(MappingSchema);\n","import { z } from '@walkeros/core/dev';\nimport { ApiVersionSchema, EventIdSchema } from './primitives';\n\nexport const SettingsSchema = z.object({\n pixelId: z\n .string()\n .min(1)\n .describe('X Pixel ID used in the Conversions API endpoint URL'),\n eventId: EventIdSchema.describe(\n 'Default pre-registered conversion event ID (like tw-xxxxx-xxxxx)',\n ),\n consumerKey: z\n .string()\n .min(1)\n .describe('OAuth 1.0a API Key (Consumer Key) for X Ads API'),\n consumerSecret: z\n .string()\n .min(1)\n .describe('OAuth 1.0a API Key Secret (Consumer Secret)'),\n accessToken: z.string().min(1).describe('OAuth 1.0a User Access Token'),\n accessTokenSecret: z\n .string()\n .min(1)\n .describe('OAuth 1.0a User Access Token Secret'),\n apiVersion: ApiVersionSchema.describe(\n 'X Ads API version number (like \"12\")',\n ).optional(),\n doNotHash: z\n .array(z.string())\n .describe(\n \"Array of user data fields that should not be hashed (like ['email'])\",\n )\n .optional(),\n url: z\n .string()\n .url()\n .describe(\n 'Custom base URL for the X Conversions API endpoint (like https://ads-api.x.com/)',\n )\n .optional(),\n user_data: z\n .record(z.string(), z.string())\n .describe(\n \"Mapping configuration for user identifiers (like { email: 'user.email', twclid: 'context.twclid' })\",\n )\n .optional(),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\n/**\n * X (Twitter) Identifier Type Enum\n * Types of user identifiers supported by X Conversions API.\n * Primary: twclid, hashed_email, hashed_phone_number.\n * Secondary: ip_address, user_agent.\n */\nexport const IdentifierTypeSchema = z.enum([\n 'twclid',\n 'hashed_email',\n 'hashed_phone_number',\n 'ip_address',\n 'user_agent',\n]);\n\n/**\n * X Event ID Schema\n * Pre-registered conversion event IDs follow the pattern `tw-xxxxx-xxxxx`.\n */\nexport const EventIdSchema = z\n .string()\n .regex(\n /^tw-[a-z0-9]+-[a-z0-9]+$/,\n 'Event ID must match X format (e.g. tw-xxxxx-xxxxx)',\n );\n\n/**\n * API Version Schema\n * X Ads API version number (numeric string).\n */\nexport const ApiVersionSchema = z\n .string()\n .regex(/^\\d+$/, 'API version must be numeric (e.g. \"12\")');\n","import { z } from '@walkeros/core/dev';\n\n/**\n * X (Twitter) Conversions API Mapping Schema\n *\n * Per-event override for the conversion event ID and monetary details.\n */\nexport const MappingSchema = z.object({\n eventId: z\n .string()\n .describe('Override the default conversion event ID for this event')\n .optional(),\n value: z\n .union([z.string(), z.number()])\n .describe('Conversion monetary value (sent to X as a string)')\n .optional(),\n currency: z\n .string()\n .describe('ISO 4217 currency code (like USD, EUR)')\n .optional(),\n number_items: z\n .number()\n .int()\n .describe('Number of items in the conversion')\n .optional(),\n description: z\n .string()\n .describe('Free-text description of the conversion')\n .optional(),\n});\n\nexport type Mapping = z.infer<typeof MappingSchema>;\n","export * as env from './env';\nexport * as step from './step';\n","import type { SendDataValue, SendResponse } from '@walkeros/core';\nimport type { SendServerOptions } from '@walkeros/server-core';\nimport type { Env } from '../types';\n\n/**\n * Example environment configurations for X (Twitter) Conversions API destination\n *\n * These environments provide standardized mock structures for testing\n * and development without requiring actual HTTP requests.\n */\n\ntype SendServerFn = (\n url: string,\n data?: SendDataValue,\n options?: SendServerOptions,\n) => Promise<SendResponse>;\n\n/**\n * Mock sendServer function that simulates successful HTTP responses\n */\nconst mockSendServer: SendServerFn = async () => {\n return {\n ok: true,\n data: { request_id: 'mock-request-id' },\n };\n};\n\n/**\n * Standard mock environment for push operations\n *\n * Use this for testing X Conversions API events without making\n * actual HTTP requests to X's servers.\n */\nexport const push: Env = {\n sendServer: mockSendServer,\n};\n\nexport const simulation = ['sendServer'];\n","import type { Flow } from '@walkeros/core';\nimport { getEvent } from '@walkeros/core';\n\n/**\n * X (Twitter) Conversions API step examples.\n *\n * At push time, the destination calls\n * `env.sendServer(url, body, { headers })` where:\n * - `url` is `${settings.url}${apiVersion}/measurement/conversions/${pixelId}`\n * - `body` is the JSON-stringified `{ conversions: [conversion] }` payload\n * - `headers` is `{ Authorization: '<OAuth-1.0a>', 'Content-Type': 'application/json' }`\n *\n * The test fixture pins `pixelId = 'o8z6j'`, `eventId = 'tw-o8z6j-o8z21'`.\n * OAuth 1.0a signing is non-deterministic (nonce + timestamp), so the test\n * normalizes the `Authorization` header to a stable marker before comparing.\n *\n * Body fields are emitted in the order the destination constructs them\n * (insertion order matters for `JSON.stringify` string equality):\n * conversion_time, event_id, identifiers, conversion_id, value? (string).\n *\n * Emails are normalized (trim + lowercase) and hashed with SHA-256.\n */\nconst ENDPOINT = 'https://ads-api.x.com/12/measurement/conversions/o8z6j';\nconst HEADERS = {\n headers: {\n Authorization: '<OAUTH_SIGNATURE>',\n 'Content-Type': 'application/json',\n },\n};\n\nexport const purchase: Flow.StepExample = {\n title: 'Purchase',\n description:\n 'A completed order is sent to the X (Twitter) Conversions API with conversion value and the hashed email.',\n in: getEvent('order complete', {\n timestamp: 1700000900000,\n data: { total: 249.99, currency: 'EUR' },\n user: { email: 'jane@example.com' },\n source: { type: 'server', id: 'https://shop.example.com', previous_id: '' },\n }),\n mapping: {\n settings: {\n value: 'data.total',\n currency: { key: 'data.currency', value: 'EUR' },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n conversions: [\n {\n conversion_time: '2023-11-14T22:28:20.000Z',\n event_id: 'tw-o8z6j-o8z21',\n identifiers: [\n {\n hashed_email:\n '8c87b489ce35cf2e2f39f80e282cb2e804932a56a213983eeeb428407d43b52d',\n },\n ],\n conversion_id: '1700000900000-gr0up-1',\n value: '249.99',\n },\n ],\n }),\n HEADERS,\n ],\n ],\n};\n\nexport const lead: Flow.StepExample = {\n title: 'Lead',\n description:\n 'A form submission is sent to X as a conversion with the SHA-256 hashed email as the identifier.',\n in: getEvent('form submit', {\n timestamp: 1700000901000,\n user: { email: 'user@example.com' },\n source: { type: 'server', id: 'https://example.com', previous_id: '' },\n }),\n mapping: undefined,\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n conversions: [\n {\n conversion_time: '2023-11-14T22:28:21.000Z',\n event_id: 'tw-o8z6j-o8z21',\n identifiers: [\n {\n hashed_email:\n 'b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514',\n },\n ],\n conversion_id: '1700000901000-gr0up-1',\n },\n ],\n }),\n HEADERS,\n ],\n ],\n};\n\nexport const purchaseWithTwclid: Flow.StepExample = {\n title: 'Purchase with twclid',\n description:\n 'A purchase is sent to X with both the hashed email and the twclid click id for ads attribution.',\n in: getEvent('order complete', {\n timestamp: 1700000902000,\n data: { total: 89.99, currency: 'USD' },\n user: { email: 'buyer@co.com' },\n context: { twclid: ['23opevjt88psuo13lu8d020qkn', 0] },\n source: { type: 'server', id: 'https://shop.example.com', previous_id: '' },\n }),\n mapping: {\n settings: {\n value: 'data.total',\n currency: { key: 'data.currency', value: 'USD' },\n },\n data: {\n map: {\n user_data: {\n map: {\n twclid: 'context.twclid',\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n conversions: [\n {\n conversion_time: '2023-11-14T22:28:22.000Z',\n event_id: 'tw-o8z6j-o8z21',\n identifiers: [\n {\n hashed_email:\n '484c39bfb51212665d9673805c112b5ba04cbf0460b6d3f00bcdc18b92afed66',\n },\n { twclid: '23opevjt88psuo13lu8d020qkn' },\n ],\n conversion_id: '1700000902000-gr0up-1',\n value: '89.99',\n },\n ],\n }),\n HEADERS,\n ],\n ],\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,cAA4B;;;ACA5B,IAAAC,cAAkB;;;ACAlB,iBAAkB;AAQX,IAAM,uBAAuB,aAAE,KAAK;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMM,IAAM,gBAAgB,aAC1B,OAAO,EACP;AAAA,EACC;AAAA,EACA;AACF;AAMK,IAAM,mBAAmB,aAC7B,OAAO,EACP,MAAM,SAAS,yCAAyC;;;AD9BpD,IAAM,iBAAiB,cAAE,OAAO;AAAA,EACrC,SAAS,cACN,OAAO,EACP,IAAI,CAAC,EACL,SAAS,qDAAqD;AAAA,EACjE,SAAS,cAAc;AAAA,IACrB;AAAA,EACF;AAAA,EACA,aAAa,cACV,OAAO,EACP,IAAI,CAAC,EACL,SAAS,iDAAiD;AAAA,EAC7D,gBAAgB,cACb,OAAO,EACP,IAAI,CAAC,EACL,SAAS,6CAA6C;AAAA,EACzD,aAAa,cAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,8BAA8B;AAAA,EACtE,mBAAmB,cAChB,OAAO,EACP,IAAI,CAAC,EACL,SAAS,qCAAqC;AAAA,EACjD,YAAY,iBAAiB;AAAA,IAC3B;AAAA,EACF,EAAE,SAAS;AAAA,EACX,WAAW,cACR,MAAM,cAAE,OAAO,CAAC,EAChB;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,KAAK,cACF,OAAO,EACP,IAAI,EACJ;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,WAAW,cACR,OAAO,cAAE,OAAO,GAAG,cAAE,OAAO,CAAC,EAC7B;AAAA,IACC;AAAA,EACF,EACC,SAAS;AACd,CAAC;;;AE9CD,IAAAC,cAAkB;AAOX,IAAM,gBAAgB,cAAE,OAAO;AAAA,EACpC,SAAS,cACN,OAAO,EACP,SAAS,yDAAyD,EAClE,SAAS;AAAA,EACZ,OAAO,cACJ,MAAM,CAAC,cAAE,OAAO,GAAG,cAAE,OAAO,CAAC,CAAC,EAC9B,SAAS,mDAAmD,EAC5D,SAAS;AAAA,EACZ,UAAU,cACP,OAAO,EACP,SAAS,wCAAwC,EACjD,SAAS;AAAA,EACZ,cAAc,cACX,OAAO,EACP,IAAI,EACJ,SAAS,mCAAmC,EAC5C,SAAS;AAAA,EACZ,aAAa,cACV,OAAO,EACP,SAAS,yCAAyC,EAClD,SAAS;AACd,CAAC;;;AHnBM,IAAM,eAAW,yBAAY,cAAc;AAC3C,IAAM,cAAU,yBAAY,aAAa;;;AIXhD;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAoBA,IAAM,iBAA+B,YAAY;AAC/C,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM,EAAE,YAAY,kBAAkB;AAAA,EACxC;AACF;AAQO,IAAM,OAAY;AAAA,EACvB,YAAY;AACd;AAEO,IAAM,aAAa,CAAC,YAAY;;;ACrCvC;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,kBAAyB;AAqBzB,IAAM,WAAW;AACjB,IAAM,UAAU;AAAA,EACd,SAAS;AAAA,IACP,eAAe;AAAA,IACf,gBAAgB;AAAA,EAClB;AACF;AAEO,IAAM,WAA6B;AAAA,EACxC,OAAO;AAAA,EACP,aACE;AAAA,EACF,QAAI,sBAAS,kBAAkB;AAAA,IAC7B,WAAW;AAAA,IACX,MAAM,EAAE,OAAO,QAAQ,UAAU,MAAM;AAAA,IACvC,MAAM,EAAE,OAAO,mBAAmB;AAAA,IAClC,QAAQ,EAAE,MAAM,UAAU,IAAI,4BAA4B,aAAa,GAAG;AAAA,EAC5E,CAAC;AAAA,EACD,SAAS;AAAA,IACP,UAAU;AAAA,MACR,OAAO;AAAA,MACP,UAAU,EAAE,KAAK,iBAAiB,OAAO,MAAM;AAAA,IACjD;AAAA,EACF;AAAA,EACA,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,MACA,KAAK,UAAU;AAAA,QACb,aAAa;AAAA,UACX;AAAA,YACE,iBAAiB;AAAA,YACjB,UAAU;AAAA,YACV,aAAa;AAAA,cACX;AAAA,gBACE,cACE;AAAA,cACJ;AAAA,YACF;AAAA,YACA,eAAe;AAAA,YACf,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,OAAyB;AAAA,EACpC,OAAO;AAAA,EACP,aACE;AAAA,EACF,QAAI,sBAAS,eAAe;AAAA,IAC1B,WAAW;AAAA,IACX,MAAM,EAAE,OAAO,mBAAmB;AAAA,IAClC,QAAQ,EAAE,MAAM,UAAU,IAAI,uBAAuB,aAAa,GAAG;AAAA,EACvE,CAAC;AAAA,EACD,SAAS;AAAA,EACT,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,MACA,KAAK,UAAU;AAAA,QACb,aAAa;AAAA,UACX;AAAA,YACE,iBAAiB;AAAA,YACjB,UAAU;AAAA,YACV,aAAa;AAAA,cACX;AAAA,gBACE,cACE;AAAA,cACJ;AAAA,YACF;AAAA,YACA,eAAe;AAAA,UACjB;AAAA,QACF;AAAA,MACF,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,qBAAuC;AAAA,EAClD,OAAO;AAAA,EACP,aACE;AAAA,EACF,QAAI,sBAAS,kBAAkB;AAAA,IAC7B,WAAW;AAAA,IACX,MAAM,EAAE,OAAO,OAAO,UAAU,MAAM;AAAA,IACtC,MAAM,EAAE,OAAO,eAAe;AAAA,IAC9B,SAAS,EAAE,QAAQ,CAAC,8BAA8B,CAAC,EAAE;AAAA,IACrD,QAAQ,EAAE,MAAM,UAAU,IAAI,4BAA4B,aAAa,GAAG;AAAA,EAC5E,CAAC;AAAA,EACD,SAAS;AAAA,IACP,UAAU;AAAA,MACR,OAAO;AAAA,MACP,UAAU,EAAE,KAAK,iBAAiB,OAAO,MAAM;AAAA,IACjD;AAAA,IACA,MAAM;AAAA,MACJ,KAAK;AAAA,QACH,WAAW;AAAA,UACT,KAAK;AAAA,YACH,QAAQ;AAAA,UACV;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,MACA,KAAK,UAAU;AAAA,QACb,aAAa;AAAA,UACX;AAAA,YACE,iBAAiB;AAAA,YACjB,UAAU;AAAA,YACV,aAAa;AAAA,cACX;AAAA,gBACE,cACE;AAAA,cACJ;AAAA,cACA,EAAE,QAAQ,6BAA6B;AAAA,YACzC;AAAA,YACA,eAAe;AAAA,YACf,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AACF;","names":["import_dev","import_dev","import_dev"]}
1
+ {"version":3,"sources":["../src/dev.ts","../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/primitives.ts","../src/schemas/mapping.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts"],"sourcesContent":["export * as schemas from './schemas';\nexport * as examples from './examples';\n","import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { MappingSchema } from './mapping';\n\nexport * from './primitives';\n\nexport { SettingsSchema, type Settings } from './settings';\nexport { MappingSchema, type Mapping } from './mapping';\n\n// JSON Schema\nexport const settings = zodToSchema(SettingsSchema);\nexport const mapping = zodToSchema(MappingSchema);\n","import { z } from '@walkeros/core/dev';\nimport { ApiVersionSchema, EventIdSchema } from './primitives';\n\nexport const SettingsSchema = z.object({\n pixelId: z\n .string()\n .min(1)\n .describe('X Pixel ID used in the Conversions API endpoint URL'),\n eventId: EventIdSchema.describe(\n 'Default pre-registered conversion event ID (like tw-xxxxx-xxxxx)',\n ),\n consumerKey: z\n .string()\n .min(1)\n .describe('OAuth 1.0a API Key (Consumer Key) for X Ads API'),\n consumerSecret: z\n .string()\n .min(1)\n .describe('OAuth 1.0a API Key Secret (Consumer Secret)'),\n accessToken: z.string().min(1).describe('OAuth 1.0a User Access Token'),\n accessTokenSecret: z\n .string()\n .min(1)\n .describe('OAuth 1.0a User Access Token Secret'),\n apiVersion: ApiVersionSchema.describe(\n 'X Ads API version number (like \"12\")',\n ).optional(),\n doNotHash: z\n .array(z.string())\n .describe(\n \"Array of user data fields that should not be hashed (like ['email'])\",\n )\n .optional(),\n url: z\n .string()\n .url()\n .describe(\n 'Custom base URL for the X Conversions API endpoint (like https://ads-api.x.com/)',\n )\n .optional(),\n user_data: z\n .record(z.string(), z.string())\n .describe(\n \"Mapping configuration for user identifiers (like { email: 'user.email', twclid: 'context.twclid' })\",\n )\n .optional(),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\n/**\n * X (Twitter) Identifier Type Enum\n * Types of user identifiers supported by X Conversions API.\n * Primary: twclid, hashed_email, hashed_phone_number.\n * Secondary: ip_address, user_agent.\n */\nexport const IdentifierTypeSchema = z.enum([\n 'twclid',\n 'hashed_email',\n 'hashed_phone_number',\n 'ip_address',\n 'user_agent',\n]);\n\n/**\n * X Event ID Schema\n * Pre-registered conversion event IDs follow the pattern `tw-xxxxx-xxxxx`.\n */\nexport const EventIdSchema = z\n .string()\n .regex(\n /^tw-[a-z0-9]+-[a-z0-9]+$/,\n 'Event ID must match X format (e.g. tw-xxxxx-xxxxx)',\n );\n\n/**\n * API Version Schema\n * X Ads API version number (numeric string).\n */\nexport const ApiVersionSchema = z\n .string()\n .regex(/^\\d+$/, 'API version must be numeric (e.g. \"12\")');\n","import { z } from '@walkeros/core/dev';\n\n/**\n * X (Twitter) Conversions API Mapping Schema\n *\n * Per-event override for the conversion event ID and monetary details.\n */\nexport const MappingSchema = z.object({\n eventId: z\n .string()\n .describe('Override the default conversion event ID for this event')\n .optional(),\n value: z\n .union([z.string(), z.number()])\n .describe('Conversion monetary value (sent to X as a string)')\n .optional(),\n currency: z\n .string()\n .describe('ISO 4217 currency code (like USD, EUR)')\n .optional(),\n number_items: z\n .number()\n .int()\n .describe('Number of items in the conversion')\n .optional(),\n description: z\n .string()\n .describe('Free-text description of the conversion')\n .optional(),\n});\n\nexport type Mapping = z.infer<typeof MappingSchema>;\n","export * as env from './env';\nexport * as step from './step';\n","import type { SendDataValue, SendResponse } from '@walkeros/core';\nimport type { SendServerOptions } from '@walkeros/server-core';\nimport type { Env } from '../types';\n\n/**\n * Example environment configurations for X (Twitter) Conversions API destination\n *\n * These environments provide standardized mock structures for testing\n * and development without requiring actual HTTP requests.\n */\n\ntype SendServerFn = (\n url: string,\n data?: SendDataValue,\n options?: SendServerOptions,\n) => Promise<SendResponse>;\n\n/**\n * Mock sendServer function that simulates successful HTTP responses\n */\nconst mockSendServer: SendServerFn = async () => {\n return {\n ok: true,\n data: { request_id: 'mock-request-id' },\n };\n};\n\n/**\n * Standard mock environment for push operations\n *\n * Use this for testing X Conversions API events without making\n * actual HTTP requests to X's servers.\n */\nexport const push: Env = {\n sendServer: mockSendServer,\n};\n\nexport const simulation = ['sendServer'];\n","import type { Flow } from '@walkeros/core';\nimport { getEvent } from '@walkeros/core';\n\n/**\n * X (Twitter) Conversions API step examples.\n *\n * At push time, the destination calls\n * `env.sendServer(url, body, { headers })` where:\n * - `url` is `${settings.url}${apiVersion}/measurement/conversions/${pixelId}`\n * - `body` is the JSON-stringified `{ conversions: [conversion] }` payload\n * - `headers` is `{ Authorization: '<OAuth-1.0a>', 'Content-Type': 'application/json' }`\n *\n * The test fixture pins `pixelId = 'o8z6j'`, `eventId = 'tw-o8z6j-o8z21'`.\n * OAuth 1.0a signing is non-deterministic (nonce + timestamp), so the test\n * normalizes the `Authorization` header to a stable marker before comparing.\n *\n * Body fields are emitted in the order the destination constructs them\n * (insertion order matters for `JSON.stringify` string equality):\n * conversion_time, event_id, identifiers, conversion_id, value? (string).\n *\n * Emails are normalized (trim + lowercase) and hashed with SHA-256.\n */\nconst ENDPOINT = 'https://ads-api.x.com/12/measurement/conversions/o8z6j';\nconst HEADERS = {\n headers: {\n Authorization: '<OAUTH_SIGNATURE>',\n 'Content-Type': 'application/json',\n },\n};\n\nexport const purchase: Flow.StepExample = {\n title: 'Purchase',\n description:\n 'A completed order is sent to the X (Twitter) Conversions API with conversion value and the hashed email.',\n in: getEvent('order complete', {\n id: 'ev-1700000900000',\n timestamp: 1700000900000,\n data: { total: 249.99, currency: 'EUR' },\n user: { email: 'jane@example.com' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n settings: {\n value: 'data.total',\n currency: { key: 'data.currency', value: 'EUR' },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n conversions: [\n {\n conversion_time: '2023-11-14T22:28:20.000Z',\n event_id: 'tw-o8z6j-o8z21',\n identifiers: [\n {\n hashed_email:\n '8c87b489ce35cf2e2f39f80e282cb2e804932a56a213983eeeb428407d43b52d',\n },\n ],\n conversion_id: 'ev-1700000900000',\n value: '249.99',\n },\n ],\n }),\n HEADERS,\n ],\n ],\n};\n\nexport const lead: Flow.StepExample = {\n title: 'Lead',\n description:\n 'A form submission is sent to X as a conversion with the SHA-256 hashed email as the identifier.',\n in: getEvent('form submit', {\n id: 'ev-1700000901000',\n timestamp: 1700000901000,\n user: { email: 'user@example.com' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: undefined,\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n conversions: [\n {\n conversion_time: '2023-11-14T22:28:21.000Z',\n event_id: 'tw-o8z6j-o8z21',\n identifiers: [\n {\n hashed_email:\n 'b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514',\n },\n ],\n conversion_id: 'ev-1700000901000',\n },\n ],\n }),\n HEADERS,\n ],\n ],\n};\n\nexport const purchaseWithTwclid: Flow.StepExample = {\n title: 'Purchase with twclid',\n description:\n 'A purchase is sent to X with both the hashed email and the twclid click id for ads attribution.',\n in: getEvent('order complete', {\n id: 'ev-1700000902000',\n timestamp: 1700000902000,\n data: { total: 89.99, currency: 'USD' },\n user: { email: 'buyer@co.com' },\n context: { twclid: ['23opevjt88psuo13lu8d020qkn', 0] },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n settings: {\n value: 'data.total',\n currency: { key: 'data.currency', value: 'USD' },\n },\n data: {\n map: {\n user_data: {\n map: {\n twclid: 'context.twclid',\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n conversions: [\n {\n conversion_time: '2023-11-14T22:28:22.000Z',\n event_id: 'tw-o8z6j-o8z21',\n identifiers: [\n {\n hashed_email:\n '484c39bfb51212665d9673805c112b5ba04cbf0460b6d3f00bcdc18b92afed66',\n },\n { twclid: '23opevjt88psuo13lu8d020qkn' },\n ],\n conversion_id: 'ev-1700000902000',\n value: '89.99',\n },\n ],\n }),\n HEADERS,\n ],\n ],\n};\n"],"mappings":"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","names":["import_dev","import_dev","import_dev"]}
package/dist/dev.mjs CHANGED
@@ -1 +1 @@
1
- var e=Object.defineProperty,i=(i,t)=>{for(var r in t)e(i,r,{get:t[r],enumerable:!0})},t={};i(t,{ApiVersionSchema:()=>c,EventIdSchema:()=>a,IdentifierTypeSchema:()=>n,MappingSchema:()=>p,SettingsSchema:()=>d,mapping:()=>l,settings:()=>u});import{zodToSchema as r}from"@walkeros/core/dev";import{z as s}from"@walkeros/core/dev";import{z as o}from"@walkeros/core/dev";var n=o.enum(["twclid","hashed_email","hashed_phone_number","ip_address","user_agent"]),a=o.string().regex(/^tw-[a-z0-9]+-[a-z0-9]+$/,"Event ID must match X format (e.g. tw-xxxxx-xxxxx)"),c=o.string().regex(/^\d+$/,'API version must be numeric (e.g. "12")'),d=s.object({pixelId:s.string().min(1).describe("X Pixel ID used in the Conversions API endpoint URL"),eventId:a.describe("Default pre-registered conversion event ID (like tw-xxxxx-xxxxx)"),consumerKey:s.string().min(1).describe("OAuth 1.0a API Key (Consumer Key) for X Ads API"),consumerSecret:s.string().min(1).describe("OAuth 1.0a API Key Secret (Consumer Secret)"),accessToken:s.string().min(1).describe("OAuth 1.0a User Access Token"),accessTokenSecret:s.string().min(1).describe("OAuth 1.0a User Access Token Secret"),apiVersion:c.describe('X Ads API version number (like "12")').optional(),doNotHash:s.array(s.string()).describe("Array of user data fields that should not be hashed (like ['email'])").optional(),url:s.string().url().describe("Custom base URL for the X Conversions API endpoint (like https://ads-api.x.com/)").optional(),user_data:s.record(s.string(),s.string()).describe("Mapping configuration for user identifiers (like { email: 'user.email', twclid: 'context.twclid' })").optional()});import{z as m}from"@walkeros/core/dev";var p=m.object({eventId:m.string().describe("Override the default conversion event ID for this event").optional(),value:m.union([m.string(),m.number()]).describe("Conversion monetary value (sent to X as a string)").optional(),currency:m.string().describe("ISO 4217 currency code (like USD, EUR)").optional(),number_items:m.number().int().describe("Number of items in the conversion").optional(),description:m.string().describe("Free-text description of the conversion").optional()}),u=r(d),l=r(p),v={};i(v,{env:()=>h,step:()=>g});var h={};i(h,{push:()=>b,simulation:()=>f});var b={sendServer:async()=>({ok:!0,data:{request_id:"mock-request-id"}})},f=["sendServer"],g={};i(g,{lead:()=>S,purchase:()=>A,purchaseWithTwclid:()=>_});import{getEvent as x}from"@walkeros/core";var y="https://ads-api.x.com/12/measurement/conversions/o8z6j",w={headers:{Authorization:"<OAUTH_SIGNATURE>","Content-Type":"application/json"}},A={title:"Purchase",description:"A completed order is sent to the X (Twitter) Conversions API with conversion value and the hashed email.",in:x("order complete",{timestamp:17000009e5,data:{total:249.99,currency:"EUR"},user:{email:"jane@example.com"},source:{type:"server",id:"https://shop.example.com",previous_id:""}}),mapping:{settings:{value:"data.total",currency:{key:"data.currency",value:"EUR"}}},out:[["sendServer",y,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:20.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"8c87b489ce35cf2e2f39f80e282cb2e804932a56a213983eeeb428407d43b52d"}],conversion_id:"1700000900000-gr0up-1",value:"249.99"}]}),w]]},S={title:"Lead",description:"A form submission is sent to X as a conversion with the SHA-256 hashed email as the identifier.",in:x("form submit",{timestamp:1700000901e3,user:{email:"user@example.com"},source:{type:"server",id:"https://example.com",previous_id:""}}),mapping:void 0,out:[["sendServer",y,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:21.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514"}],conversion_id:"1700000901000-gr0up-1"}]}),w]]},_={title:"Purchase with twclid",description:"A purchase is sent to X with both the hashed email and the twclid click id for ads attribution.",in:x("order complete",{timestamp:1700000902e3,data:{total:89.99,currency:"USD"},user:{email:"buyer@co.com"},context:{twclid:["23opevjt88psuo13lu8d020qkn",0]},source:{type:"server",id:"https://shop.example.com",previous_id:""}}),mapping:{settings:{value:"data.total",currency:{key:"data.currency",value:"USD"}},data:{map:{user_data:{map:{twclid:"context.twclid"}}}}},out:[["sendServer",y,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:22.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"484c39bfb51212665d9673805c112b5ba04cbf0460b6d3f00bcdc18b92afed66"},{twclid:"23opevjt88psuo13lu8d020qkn"}],conversion_id:"1700000902000-gr0up-1",value:"89.99"}]}),w]]};export{v as examples,t as schemas};//# sourceMappingURL=dev.mjs.map
1
+ var e=Object.defineProperty,i=(i,r)=>{for(var t in r)e(i,t,{get:r[t],enumerable:!0})},r={};i(r,{ApiVersionSchema:()=>c,EventIdSchema:()=>a,IdentifierTypeSchema:()=>o,MappingSchema:()=>l,SettingsSchema:()=>d,mapping:()=>p,settings:()=>u});import{zodToSchema as t}from"@walkeros/core/dev";import{z as s}from"@walkeros/core/dev";import{z as n}from"@walkeros/core/dev";var o=n.enum(["twclid","hashed_email","hashed_phone_number","ip_address","user_agent"]),a=n.string().regex(/^tw-[a-z0-9]+-[a-z0-9]+$/,"Event ID must match X format (e.g. tw-xxxxx-xxxxx)"),c=n.string().regex(/^\d+$/,'API version must be numeric (e.g. "12")'),d=s.object({pixelId:s.string().min(1).describe("X Pixel ID used in the Conversions API endpoint URL"),eventId:a.describe("Default pre-registered conversion event ID (like tw-xxxxx-xxxxx)"),consumerKey:s.string().min(1).describe("OAuth 1.0a API Key (Consumer Key) for X Ads API"),consumerSecret:s.string().min(1).describe("OAuth 1.0a API Key Secret (Consumer Secret)"),accessToken:s.string().min(1).describe("OAuth 1.0a User Access Token"),accessTokenSecret:s.string().min(1).describe("OAuth 1.0a User Access Token Secret"),apiVersion:c.describe('X Ads API version number (like "12")').optional(),doNotHash:s.array(s.string()).describe("Array of user data fields that should not be hashed (like ['email'])").optional(),url:s.string().url().describe("Custom base URL for the X Conversions API endpoint (like https://ads-api.x.com/)").optional(),user_data:s.record(s.string(),s.string()).describe("Mapping configuration for user identifiers (like { email: 'user.email', twclid: 'context.twclid' })").optional()});import{z as m}from"@walkeros/core/dev";var l=m.object({eventId:m.string().describe("Override the default conversion event ID for this event").optional(),value:m.union([m.string(),m.number()]).describe("Conversion monetary value (sent to X as a string)").optional(),currency:m.string().describe("ISO 4217 currency code (like USD, EUR)").optional(),number_items:m.number().int().describe("Number of items in the conversion").optional(),description:m.string().describe("Free-text description of the conversion").optional()}),u=t(d),p=t(l),v={};i(v,{env:()=>h,step:()=>g});var h={};i(h,{push:()=>b,simulation:()=>f});var b={sendServer:async()=>({ok:!0,data:{request_id:"mock-request-id"}})},f=["sendServer"],g={};i(g,{lead:()=>S,purchase:()=>A,purchaseWithTwclid:()=>k});import{getEvent as x}from"@walkeros/core";var y="https://ads-api.x.com/12/measurement/conversions/o8z6j",w={headers:{Authorization:"<OAUTH_SIGNATURE>","Content-Type":"application/json"}},A={title:"Purchase",description:"A completed order is sent to the X (Twitter) Conversions API with conversion value and the hashed email.",in:x("order complete",{id:"ev-1700000900000",timestamp:17000009e5,data:{total:249.99,currency:"EUR"},user:{email:"jane@example.com"},source:{type:"express",platform:"server"}}),mapping:{settings:{value:"data.total",currency:{key:"data.currency",value:"EUR"}}},out:[["sendServer",y,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:20.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"8c87b489ce35cf2e2f39f80e282cb2e804932a56a213983eeeb428407d43b52d"}],conversion_id:"ev-1700000900000",value:"249.99"}]}),w]]},S={title:"Lead",description:"A form submission is sent to X as a conversion with the SHA-256 hashed email as the identifier.",in:x("form submit",{id:"ev-1700000901000",timestamp:1700000901e3,user:{email:"user@example.com"},source:{type:"express",platform:"server"}}),mapping:void 0,out:[["sendServer",y,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:21.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514"}],conversion_id:"ev-1700000901000"}]}),w]]},k={title:"Purchase with twclid",description:"A purchase is sent to X with both the hashed email and the twclid click id for ads attribution.",in:x("order complete",{id:"ev-1700000902000",timestamp:1700000902e3,data:{total:89.99,currency:"USD"},user:{email:"buyer@co.com"},context:{twclid:["23opevjt88psuo13lu8d020qkn",0]},source:{type:"express",platform:"server"}}),mapping:{settings:{value:"data.total",currency:{key:"data.currency",value:"USD"}},data:{map:{user_data:{map:{twclid:"context.twclid"}}}}},out:[["sendServer",y,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:22.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"484c39bfb51212665d9673805c112b5ba04cbf0460b6d3f00bcdc18b92afed66"},{twclid:"23opevjt88psuo13lu8d020qkn"}],conversion_id:"ev-1700000902000",value:"89.99"}]}),w]]};export{v as examples,r as schemas};//# sourceMappingURL=dev.mjs.map
package/dist/dev.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/primitives.ts","../src/schemas/mapping.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts"],"sourcesContent":["import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { MappingSchema } from './mapping';\n\nexport * from './primitives';\n\nexport { SettingsSchema, type Settings } from './settings';\nexport { MappingSchema, type Mapping } from './mapping';\n\n// JSON Schema\nexport const settings = zodToSchema(SettingsSchema);\nexport const mapping = zodToSchema(MappingSchema);\n","import { z } from '@walkeros/core/dev';\nimport { ApiVersionSchema, EventIdSchema } from './primitives';\n\nexport const SettingsSchema = z.object({\n pixelId: z\n .string()\n .min(1)\n .describe('X Pixel ID used in the Conversions API endpoint URL'),\n eventId: EventIdSchema.describe(\n 'Default pre-registered conversion event ID (like tw-xxxxx-xxxxx)',\n ),\n consumerKey: z\n .string()\n .min(1)\n .describe('OAuth 1.0a API Key (Consumer Key) for X Ads API'),\n consumerSecret: z\n .string()\n .min(1)\n .describe('OAuth 1.0a API Key Secret (Consumer Secret)'),\n accessToken: z.string().min(1).describe('OAuth 1.0a User Access Token'),\n accessTokenSecret: z\n .string()\n .min(1)\n .describe('OAuth 1.0a User Access Token Secret'),\n apiVersion: ApiVersionSchema.describe(\n 'X Ads API version number (like \"12\")',\n ).optional(),\n doNotHash: z\n .array(z.string())\n .describe(\n \"Array of user data fields that should not be hashed (like ['email'])\",\n )\n .optional(),\n url: z\n .string()\n .url()\n .describe(\n 'Custom base URL for the X Conversions API endpoint (like https://ads-api.x.com/)',\n )\n .optional(),\n user_data: z\n .record(z.string(), z.string())\n .describe(\n \"Mapping configuration for user identifiers (like { email: 'user.email', twclid: 'context.twclid' })\",\n )\n .optional(),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\n/**\n * X (Twitter) Identifier Type Enum\n * Types of user identifiers supported by X Conversions API.\n * Primary: twclid, hashed_email, hashed_phone_number.\n * Secondary: ip_address, user_agent.\n */\nexport const IdentifierTypeSchema = z.enum([\n 'twclid',\n 'hashed_email',\n 'hashed_phone_number',\n 'ip_address',\n 'user_agent',\n]);\n\n/**\n * X Event ID Schema\n * Pre-registered conversion event IDs follow the pattern `tw-xxxxx-xxxxx`.\n */\nexport const EventIdSchema = z\n .string()\n .regex(\n /^tw-[a-z0-9]+-[a-z0-9]+$/,\n 'Event ID must match X format (e.g. tw-xxxxx-xxxxx)',\n );\n\n/**\n * API Version Schema\n * X Ads API version number (numeric string).\n */\nexport const ApiVersionSchema = z\n .string()\n .regex(/^\\d+$/, 'API version must be numeric (e.g. \"12\")');\n","import { z } from '@walkeros/core/dev';\n\n/**\n * X (Twitter) Conversions API Mapping Schema\n *\n * Per-event override for the conversion event ID and monetary details.\n */\nexport const MappingSchema = z.object({\n eventId: z\n .string()\n .describe('Override the default conversion event ID for this event')\n .optional(),\n value: z\n .union([z.string(), z.number()])\n .describe('Conversion monetary value (sent to X as a string)')\n .optional(),\n currency: z\n .string()\n .describe('ISO 4217 currency code (like USD, EUR)')\n .optional(),\n number_items: z\n .number()\n .int()\n .describe('Number of items in the conversion')\n .optional(),\n description: z\n .string()\n .describe('Free-text description of the conversion')\n .optional(),\n});\n\nexport type Mapping = z.infer<typeof MappingSchema>;\n","export * as env from './env';\nexport * as step from './step';\n","import type { SendDataValue, SendResponse } from '@walkeros/core';\nimport type { SendServerOptions } from '@walkeros/server-core';\nimport type { Env } from '../types';\n\n/**\n * Example environment configurations for X (Twitter) Conversions API destination\n *\n * These environments provide standardized mock structures for testing\n * and development without requiring actual HTTP requests.\n */\n\ntype SendServerFn = (\n url: string,\n data?: SendDataValue,\n options?: SendServerOptions,\n) => Promise<SendResponse>;\n\n/**\n * Mock sendServer function that simulates successful HTTP responses\n */\nconst mockSendServer: SendServerFn = async () => {\n return {\n ok: true,\n data: { request_id: 'mock-request-id' },\n };\n};\n\n/**\n * Standard mock environment for push operations\n *\n * Use this for testing X Conversions API events without making\n * actual HTTP requests to X's servers.\n */\nexport const push: Env = {\n sendServer: mockSendServer,\n};\n\nexport const simulation = ['sendServer'];\n","import type { Flow } from '@walkeros/core';\nimport { getEvent } from '@walkeros/core';\n\n/**\n * X (Twitter) Conversions API step examples.\n *\n * At push time, the destination calls\n * `env.sendServer(url, body, { headers })` where:\n * - `url` is `${settings.url}${apiVersion}/measurement/conversions/${pixelId}`\n * - `body` is the JSON-stringified `{ conversions: [conversion] }` payload\n * - `headers` is `{ Authorization: '<OAuth-1.0a>', 'Content-Type': 'application/json' }`\n *\n * The test fixture pins `pixelId = 'o8z6j'`, `eventId = 'tw-o8z6j-o8z21'`.\n * OAuth 1.0a signing is non-deterministic (nonce + timestamp), so the test\n * normalizes the `Authorization` header to a stable marker before comparing.\n *\n * Body fields are emitted in the order the destination constructs them\n * (insertion order matters for `JSON.stringify` string equality):\n * conversion_time, event_id, identifiers, conversion_id, value? (string).\n *\n * Emails are normalized (trim + lowercase) and hashed with SHA-256.\n */\nconst ENDPOINT = 'https://ads-api.x.com/12/measurement/conversions/o8z6j';\nconst HEADERS = {\n headers: {\n Authorization: '<OAUTH_SIGNATURE>',\n 'Content-Type': 'application/json',\n },\n};\n\nexport const purchase: Flow.StepExample = {\n title: 'Purchase',\n description:\n 'A completed order is sent to the X (Twitter) Conversions API with conversion value and the hashed email.',\n in: getEvent('order complete', {\n timestamp: 1700000900000,\n data: { total: 249.99, currency: 'EUR' },\n user: { email: 'jane@example.com' },\n source: { type: 'server', id: 'https://shop.example.com', previous_id: '' },\n }),\n mapping: {\n settings: {\n value: 'data.total',\n currency: { key: 'data.currency', value: 'EUR' },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n conversions: [\n {\n conversion_time: '2023-11-14T22:28:20.000Z',\n event_id: 'tw-o8z6j-o8z21',\n identifiers: [\n {\n hashed_email:\n '8c87b489ce35cf2e2f39f80e282cb2e804932a56a213983eeeb428407d43b52d',\n },\n ],\n conversion_id: '1700000900000-gr0up-1',\n value: '249.99',\n },\n ],\n }),\n HEADERS,\n ],\n ],\n};\n\nexport const lead: Flow.StepExample = {\n title: 'Lead',\n description:\n 'A form submission is sent to X as a conversion with the SHA-256 hashed email as the identifier.',\n in: getEvent('form submit', {\n timestamp: 1700000901000,\n user: { email: 'user@example.com' },\n source: { type: 'server', id: 'https://example.com', previous_id: '' },\n }),\n mapping: undefined,\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n conversions: [\n {\n conversion_time: '2023-11-14T22:28:21.000Z',\n event_id: 'tw-o8z6j-o8z21',\n identifiers: [\n {\n hashed_email:\n 'b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514',\n },\n ],\n conversion_id: '1700000901000-gr0up-1',\n },\n ],\n }),\n HEADERS,\n ],\n ],\n};\n\nexport const purchaseWithTwclid: Flow.StepExample = {\n title: 'Purchase with twclid',\n description:\n 'A purchase is sent to X with both the hashed email and the twclid click id for ads attribution.',\n in: getEvent('order complete', {\n timestamp: 1700000902000,\n data: { total: 89.99, currency: 'USD' },\n user: { email: 'buyer@co.com' },\n context: { twclid: ['23opevjt88psuo13lu8d020qkn', 0] },\n source: { type: 'server', id: 'https://shop.example.com', previous_id: '' },\n }),\n mapping: {\n settings: {\n value: 'data.total',\n currency: { key: 'data.currency', value: 'USD' },\n },\n data: {\n map: {\n user_data: {\n map: {\n twclid: 'context.twclid',\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n conversions: [\n {\n conversion_time: '2023-11-14T22:28:22.000Z',\n event_id: 'tw-o8z6j-o8z21',\n identifiers: [\n {\n hashed_email:\n '484c39bfb51212665d9673805c112b5ba04cbf0460b6d3f00bcdc18b92afed66',\n },\n { twclid: '23opevjt88psuo13lu8d020qkn' },\n ],\n conversion_id: '1700000902000-gr0up-1',\n value: '89.99',\n },\n ],\n }),\n HEADERS,\n ],\n ],\n};\n"],"mappings":"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","names":["z","z","z"]}
1
+ {"version":3,"sources":["../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/primitives.ts","../src/schemas/mapping.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts"],"sourcesContent":["import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { MappingSchema } from './mapping';\n\nexport * from './primitives';\n\nexport { SettingsSchema, type Settings } from './settings';\nexport { MappingSchema, type Mapping } from './mapping';\n\n// JSON Schema\nexport const settings = zodToSchema(SettingsSchema);\nexport const mapping = zodToSchema(MappingSchema);\n","import { z } from '@walkeros/core/dev';\nimport { ApiVersionSchema, EventIdSchema } from './primitives';\n\nexport const SettingsSchema = z.object({\n pixelId: z\n .string()\n .min(1)\n .describe('X Pixel ID used in the Conversions API endpoint URL'),\n eventId: EventIdSchema.describe(\n 'Default pre-registered conversion event ID (like tw-xxxxx-xxxxx)',\n ),\n consumerKey: z\n .string()\n .min(1)\n .describe('OAuth 1.0a API Key (Consumer Key) for X Ads API'),\n consumerSecret: z\n .string()\n .min(1)\n .describe('OAuth 1.0a API Key Secret (Consumer Secret)'),\n accessToken: z.string().min(1).describe('OAuth 1.0a User Access Token'),\n accessTokenSecret: z\n .string()\n .min(1)\n .describe('OAuth 1.0a User Access Token Secret'),\n apiVersion: ApiVersionSchema.describe(\n 'X Ads API version number (like \"12\")',\n ).optional(),\n doNotHash: z\n .array(z.string())\n .describe(\n \"Array of user data fields that should not be hashed (like ['email'])\",\n )\n .optional(),\n url: z\n .string()\n .url()\n .describe(\n 'Custom base URL for the X Conversions API endpoint (like https://ads-api.x.com/)',\n )\n .optional(),\n user_data: z\n .record(z.string(), z.string())\n .describe(\n \"Mapping configuration for user identifiers (like { email: 'user.email', twclid: 'context.twclid' })\",\n )\n .optional(),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\n/**\n * X (Twitter) Identifier Type Enum\n * Types of user identifiers supported by X Conversions API.\n * Primary: twclid, hashed_email, hashed_phone_number.\n * Secondary: ip_address, user_agent.\n */\nexport const IdentifierTypeSchema = z.enum([\n 'twclid',\n 'hashed_email',\n 'hashed_phone_number',\n 'ip_address',\n 'user_agent',\n]);\n\n/**\n * X Event ID Schema\n * Pre-registered conversion event IDs follow the pattern `tw-xxxxx-xxxxx`.\n */\nexport const EventIdSchema = z\n .string()\n .regex(\n /^tw-[a-z0-9]+-[a-z0-9]+$/,\n 'Event ID must match X format (e.g. tw-xxxxx-xxxxx)',\n );\n\n/**\n * API Version Schema\n * X Ads API version number (numeric string).\n */\nexport const ApiVersionSchema = z\n .string()\n .regex(/^\\d+$/, 'API version must be numeric (e.g. \"12\")');\n","import { z } from '@walkeros/core/dev';\n\n/**\n * X (Twitter) Conversions API Mapping Schema\n *\n * Per-event override for the conversion event ID and monetary details.\n */\nexport const MappingSchema = z.object({\n eventId: z\n .string()\n .describe('Override the default conversion event ID for this event')\n .optional(),\n value: z\n .union([z.string(), z.number()])\n .describe('Conversion monetary value (sent to X as a string)')\n .optional(),\n currency: z\n .string()\n .describe('ISO 4217 currency code (like USD, EUR)')\n .optional(),\n number_items: z\n .number()\n .int()\n .describe('Number of items in the conversion')\n .optional(),\n description: z\n .string()\n .describe('Free-text description of the conversion')\n .optional(),\n});\n\nexport type Mapping = z.infer<typeof MappingSchema>;\n","export * as env from './env';\nexport * as step from './step';\n","import type { SendDataValue, SendResponse } from '@walkeros/core';\nimport type { SendServerOptions } from '@walkeros/server-core';\nimport type { Env } from '../types';\n\n/**\n * Example environment configurations for X (Twitter) Conversions API destination\n *\n * These environments provide standardized mock structures for testing\n * and development without requiring actual HTTP requests.\n */\n\ntype SendServerFn = (\n url: string,\n data?: SendDataValue,\n options?: SendServerOptions,\n) => Promise<SendResponse>;\n\n/**\n * Mock sendServer function that simulates successful HTTP responses\n */\nconst mockSendServer: SendServerFn = async () => {\n return {\n ok: true,\n data: { request_id: 'mock-request-id' },\n };\n};\n\n/**\n * Standard mock environment for push operations\n *\n * Use this for testing X Conversions API events without making\n * actual HTTP requests to X's servers.\n */\nexport const push: Env = {\n sendServer: mockSendServer,\n};\n\nexport const simulation = ['sendServer'];\n","import type { Flow } from '@walkeros/core';\nimport { getEvent } from '@walkeros/core';\n\n/**\n * X (Twitter) Conversions API step examples.\n *\n * At push time, the destination calls\n * `env.sendServer(url, body, { headers })` where:\n * - `url` is `${settings.url}${apiVersion}/measurement/conversions/${pixelId}`\n * - `body` is the JSON-stringified `{ conversions: [conversion] }` payload\n * - `headers` is `{ Authorization: '<OAuth-1.0a>', 'Content-Type': 'application/json' }`\n *\n * The test fixture pins `pixelId = 'o8z6j'`, `eventId = 'tw-o8z6j-o8z21'`.\n * OAuth 1.0a signing is non-deterministic (nonce + timestamp), so the test\n * normalizes the `Authorization` header to a stable marker before comparing.\n *\n * Body fields are emitted in the order the destination constructs them\n * (insertion order matters for `JSON.stringify` string equality):\n * conversion_time, event_id, identifiers, conversion_id, value? (string).\n *\n * Emails are normalized (trim + lowercase) and hashed with SHA-256.\n */\nconst ENDPOINT = 'https://ads-api.x.com/12/measurement/conversions/o8z6j';\nconst HEADERS = {\n headers: {\n Authorization: '<OAUTH_SIGNATURE>',\n 'Content-Type': 'application/json',\n },\n};\n\nexport const purchase: Flow.StepExample = {\n title: 'Purchase',\n description:\n 'A completed order is sent to the X (Twitter) Conversions API with conversion value and the hashed email.',\n in: getEvent('order complete', {\n id: 'ev-1700000900000',\n timestamp: 1700000900000,\n data: { total: 249.99, currency: 'EUR' },\n user: { email: 'jane@example.com' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n settings: {\n value: 'data.total',\n currency: { key: 'data.currency', value: 'EUR' },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n conversions: [\n {\n conversion_time: '2023-11-14T22:28:20.000Z',\n event_id: 'tw-o8z6j-o8z21',\n identifiers: [\n {\n hashed_email:\n '8c87b489ce35cf2e2f39f80e282cb2e804932a56a213983eeeb428407d43b52d',\n },\n ],\n conversion_id: 'ev-1700000900000',\n value: '249.99',\n },\n ],\n }),\n HEADERS,\n ],\n ],\n};\n\nexport const lead: Flow.StepExample = {\n title: 'Lead',\n description:\n 'A form submission is sent to X as a conversion with the SHA-256 hashed email as the identifier.',\n in: getEvent('form submit', {\n id: 'ev-1700000901000',\n timestamp: 1700000901000,\n user: { email: 'user@example.com' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: undefined,\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n conversions: [\n {\n conversion_time: '2023-11-14T22:28:21.000Z',\n event_id: 'tw-o8z6j-o8z21',\n identifiers: [\n {\n hashed_email:\n 'b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514',\n },\n ],\n conversion_id: 'ev-1700000901000',\n },\n ],\n }),\n HEADERS,\n ],\n ],\n};\n\nexport const purchaseWithTwclid: Flow.StepExample = {\n title: 'Purchase with twclid',\n description:\n 'A purchase is sent to X with both the hashed email and the twclid click id for ads attribution.',\n in: getEvent('order complete', {\n id: 'ev-1700000902000',\n timestamp: 1700000902000,\n data: { total: 89.99, currency: 'USD' },\n user: { email: 'buyer@co.com' },\n context: { twclid: ['23opevjt88psuo13lu8d020qkn', 0] },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n settings: {\n value: 'data.total',\n currency: { key: 'data.currency', value: 'USD' },\n },\n data: {\n map: {\n user_data: {\n map: {\n twclid: 'context.twclid',\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n conversions: [\n {\n conversion_time: '2023-11-14T22:28:22.000Z',\n event_id: 'tw-o8z6j-o8z21',\n identifiers: [\n {\n hashed_email:\n '484c39bfb51212665d9673805c112b5ba04cbf0460b6d3f00bcdc18b92afed66',\n },\n { twclid: '23opevjt88psuo13lu8d020qkn' },\n ],\n conversion_id: 'ev-1700000902000',\n value: '89.99',\n },\n ],\n }),\n HEADERS,\n ],\n ],\n};\n"],"mappings":";;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,mBAAmB;;;ACA5B,SAAS,KAAAA,UAAS;;;ACAlB,SAAS,SAAS;AAQX,IAAM,uBAAuB,EAAE,KAAK;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMM,IAAM,gBAAgB,EAC1B,OAAO,EACP;AAAA,EACC;AAAA,EACA;AACF;AAMK,IAAM,mBAAmB,EAC7B,OAAO,EACP,MAAM,SAAS,yCAAyC;;;AD9BpD,IAAM,iBAAiBC,GAAE,OAAO;AAAA,EACrC,SAASA,GACN,OAAO,EACP,IAAI,CAAC,EACL,SAAS,qDAAqD;AAAA,EACjE,SAAS,cAAc;AAAA,IACrB;AAAA,EACF;AAAA,EACA,aAAaA,GACV,OAAO,EACP,IAAI,CAAC,EACL,SAAS,iDAAiD;AAAA,EAC7D,gBAAgBA,GACb,OAAO,EACP,IAAI,CAAC,EACL,SAAS,6CAA6C;AAAA,EACzD,aAAaA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,8BAA8B;AAAA,EACtE,mBAAmBA,GAChB,OAAO,EACP,IAAI,CAAC,EACL,SAAS,qCAAqC;AAAA,EACjD,YAAY,iBAAiB;AAAA,IAC3B;AAAA,EACF,EAAE,SAAS;AAAA,EACX,WAAWA,GACR,MAAMA,GAAE,OAAO,CAAC,EAChB;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,KAAKA,GACF,OAAO,EACP,IAAI,EACJ;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,WAAWA,GACR,OAAOA,GAAE,OAAO,GAAGA,GAAE,OAAO,CAAC,EAC7B;AAAA,IACC;AAAA,EACF,EACC,SAAS;AACd,CAAC;;;AE9CD,SAAS,KAAAC,UAAS;AAOX,IAAM,gBAAgBA,GAAE,OAAO;AAAA,EACpC,SAASA,GACN,OAAO,EACP,SAAS,yDAAyD,EAClE,SAAS;AAAA,EACZ,OAAOA,GACJ,MAAM,CAACA,GAAE,OAAO,GAAGA,GAAE,OAAO,CAAC,CAAC,EAC9B,SAAS,mDAAmD,EAC5D,SAAS;AAAA,EACZ,UAAUA,GACP,OAAO,EACP,SAAS,wCAAwC,EACjD,SAAS;AAAA,EACZ,cAAcA,GACX,OAAO,EACP,IAAI,EACJ,SAAS,mCAAmC,EAC5C,SAAS;AAAA,EACZ,aAAaA,GACV,OAAO,EACP,SAAS,yCAAyC,EAClD,SAAS;AACd,CAAC;;;AHnBM,IAAM,WAAW,YAAY,cAAc;AAC3C,IAAM,UAAU,YAAY,aAAa;;;AIXhD;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAoBA,IAAM,iBAA+B,YAAY;AAC/C,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM,EAAE,YAAY,kBAAkB;AAAA,EACxC;AACF;AAQO,IAAM,OAAY;AAAA,EACvB,YAAY;AACd;AAEO,IAAM,aAAa,CAAC,YAAY;;;ACrCvC;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,SAAS,gBAAgB;AAqBzB,IAAM,WAAW;AACjB,IAAM,UAAU;AAAA,EACd,SAAS;AAAA,IACP,eAAe;AAAA,IACf,gBAAgB;AAAA,EAClB;AACF;AAEO,IAAM,WAA6B;AAAA,EACxC,OAAO;AAAA,EACP,aACE;AAAA,EACF,IAAI,SAAS,kBAAkB;AAAA,IAC7B,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,MAAM,EAAE,OAAO,QAAQ,UAAU,MAAM;AAAA,IACvC,MAAM,EAAE,OAAO,mBAAmB;AAAA,IAClC,QAAQ,EAAE,MAAM,WAAW,UAAU,SAAS;AAAA,EAChD,CAAC;AAAA,EACD,SAAS;AAAA,IACP,UAAU;AAAA,MACR,OAAO;AAAA,MACP,UAAU,EAAE,KAAK,iBAAiB,OAAO,MAAM;AAAA,IACjD;AAAA,EACF;AAAA,EACA,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,MACA,KAAK,UAAU;AAAA,QACb,aAAa;AAAA,UACX;AAAA,YACE,iBAAiB;AAAA,YACjB,UAAU;AAAA,YACV,aAAa;AAAA,cACX;AAAA,gBACE,cACE;AAAA,cACJ;AAAA,YACF;AAAA,YACA,eAAe;AAAA,YACf,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,OAAyB;AAAA,EACpC,OAAO;AAAA,EACP,aACE;AAAA,EACF,IAAI,SAAS,eAAe;AAAA,IAC1B,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,MAAM,EAAE,OAAO,mBAAmB;AAAA,IAClC,QAAQ,EAAE,MAAM,WAAW,UAAU,SAAS;AAAA,EAChD,CAAC;AAAA,EACD,SAAS;AAAA,EACT,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,MACA,KAAK,UAAU;AAAA,QACb,aAAa;AAAA,UACX;AAAA,YACE,iBAAiB;AAAA,YACjB,UAAU;AAAA,YACV,aAAa;AAAA,cACX;AAAA,gBACE,cACE;AAAA,cACJ;AAAA,YACF;AAAA,YACA,eAAe;AAAA,UACjB;AAAA,QACF;AAAA,MACF,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,qBAAuC;AAAA,EAClD,OAAO;AAAA,EACP,aACE;AAAA,EACF,IAAI,SAAS,kBAAkB;AAAA,IAC7B,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,MAAM,EAAE,OAAO,OAAO,UAAU,MAAM;AAAA,IACtC,MAAM,EAAE,OAAO,eAAe;AAAA,IAC9B,SAAS,EAAE,QAAQ,CAAC,8BAA8B,CAAC,EAAE;AAAA,IACrD,QAAQ,EAAE,MAAM,WAAW,UAAU,SAAS;AAAA,EAChD,CAAC;AAAA,EACD,SAAS;AAAA,IACP,UAAU;AAAA,MACR,OAAO;AAAA,MACP,UAAU,EAAE,KAAK,iBAAiB,OAAO,MAAM;AAAA,IACjD;AAAA,IACA,MAAM;AAAA,MACJ,KAAK;AAAA,QACH,WAAW;AAAA,UACT,KAAK;AAAA,YACH,QAAQ;AAAA,UACV;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,MACA,KAAK,UAAU;AAAA,QACb,aAAa;AAAA,UACX;AAAA,YACE,iBAAiB;AAAA,YACjB,UAAU;AAAA,YACV,aAAa;AAAA,cACX;AAAA,gBACE,cACE;AAAA,cACJ;AAAA,cACA,EAAE,QAAQ,6BAA6B;AAAA,YACzC;AAAA,YACA,eAAe;AAAA,YACf,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AACF;","names":["z","z","z"]}
@@ -61,10 +61,11 @@ var purchase = {
61
61
  title: "Purchase",
62
62
  description: "A completed order is sent to the X (Twitter) Conversions API with conversion value and the hashed email.",
63
63
  in: (0, import_core.getEvent)("order complete", {
64
+ id: "ev-1700000900000",
64
65
  timestamp: 17000009e5,
65
66
  data: { total: 249.99, currency: "EUR" },
66
67
  user: { email: "jane@example.com" },
67
- source: { type: "server", id: "https://shop.example.com", previous_id: "" }
68
+ source: { type: "express", platform: "server" }
68
69
  }),
69
70
  mapping: {
70
71
  settings: {
@@ -86,7 +87,7 @@ var purchase = {
86
87
  hashed_email: "8c87b489ce35cf2e2f39f80e282cb2e804932a56a213983eeeb428407d43b52d"
87
88
  }
88
89
  ],
89
- conversion_id: "1700000900000-gr0up-1",
90
+ conversion_id: "ev-1700000900000",
90
91
  value: "249.99"
91
92
  }
92
93
  ]
@@ -99,9 +100,10 @@ var lead = {
99
100
  title: "Lead",
100
101
  description: "A form submission is sent to X as a conversion with the SHA-256 hashed email as the identifier.",
101
102
  in: (0, import_core.getEvent)("form submit", {
103
+ id: "ev-1700000901000",
102
104
  timestamp: 1700000901e3,
103
105
  user: { email: "user@example.com" },
104
- source: { type: "server", id: "https://example.com", previous_id: "" }
106
+ source: { type: "express", platform: "server" }
105
107
  }),
106
108
  mapping: void 0,
107
109
  out: [
@@ -118,7 +120,7 @@ var lead = {
118
120
  hashed_email: "b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514"
119
121
  }
120
122
  ],
121
- conversion_id: "1700000901000-gr0up-1"
123
+ conversion_id: "ev-1700000901000"
122
124
  }
123
125
  ]
124
126
  }),
@@ -130,11 +132,12 @@ var purchaseWithTwclid = {
130
132
  title: "Purchase with twclid",
131
133
  description: "A purchase is sent to X with both the hashed email and the twclid click id for ads attribution.",
132
134
  in: (0, import_core.getEvent)("order complete", {
135
+ id: "ev-1700000902000",
133
136
  timestamp: 1700000902e3,
134
137
  data: { total: 89.99, currency: "USD" },
135
138
  user: { email: "buyer@co.com" },
136
139
  context: { twclid: ["23opevjt88psuo13lu8d020qkn", 0] },
137
- source: { type: "server", id: "https://shop.example.com", previous_id: "" }
140
+ source: { type: "express", platform: "server" }
138
141
  }),
139
142
  mapping: {
140
143
  settings: {
@@ -166,7 +169,7 @@ var purchaseWithTwclid = {
166
169
  },
167
170
  { twclid: "23opevjt88psuo13lu8d020qkn" }
168
171
  ],
169
- conversion_id: "1700000902000-gr0up-1",
172
+ conversion_id: "ev-1700000902000",
170
173
  value: "89.99"
171
174
  }
172
175
  ]
@@ -40,10 +40,11 @@ var purchase = {
40
40
  title: "Purchase",
41
41
  description: "A completed order is sent to the X (Twitter) Conversions API with conversion value and the hashed email.",
42
42
  in: getEvent("order complete", {
43
+ id: "ev-1700000900000",
43
44
  timestamp: 17000009e5,
44
45
  data: { total: 249.99, currency: "EUR" },
45
46
  user: { email: "jane@example.com" },
46
- source: { type: "server", id: "https://shop.example.com", previous_id: "" }
47
+ source: { type: "express", platform: "server" }
47
48
  }),
48
49
  mapping: {
49
50
  settings: {
@@ -65,7 +66,7 @@ var purchase = {
65
66
  hashed_email: "8c87b489ce35cf2e2f39f80e282cb2e804932a56a213983eeeb428407d43b52d"
66
67
  }
67
68
  ],
68
- conversion_id: "1700000900000-gr0up-1",
69
+ conversion_id: "ev-1700000900000",
69
70
  value: "249.99"
70
71
  }
71
72
  ]
@@ -78,9 +79,10 @@ var lead = {
78
79
  title: "Lead",
79
80
  description: "A form submission is sent to X as a conversion with the SHA-256 hashed email as the identifier.",
80
81
  in: getEvent("form submit", {
82
+ id: "ev-1700000901000",
81
83
  timestamp: 1700000901e3,
82
84
  user: { email: "user@example.com" },
83
- source: { type: "server", id: "https://example.com", previous_id: "" }
85
+ source: { type: "express", platform: "server" }
84
86
  }),
85
87
  mapping: void 0,
86
88
  out: [
@@ -97,7 +99,7 @@ var lead = {
97
99
  hashed_email: "b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514"
98
100
  }
99
101
  ],
100
- conversion_id: "1700000901000-gr0up-1"
102
+ conversion_id: "ev-1700000901000"
101
103
  }
102
104
  ]
103
105
  }),
@@ -109,11 +111,12 @@ var purchaseWithTwclid = {
109
111
  title: "Purchase with twclid",
110
112
  description: "A purchase is sent to X with both the hashed email and the twclid click id for ads attribution.",
111
113
  in: getEvent("order complete", {
114
+ id: "ev-1700000902000",
112
115
  timestamp: 1700000902e3,
113
116
  data: { total: 89.99, currency: "USD" },
114
117
  user: { email: "buyer@co.com" },
115
118
  context: { twclid: ["23opevjt88psuo13lu8d020qkn", 0] },
116
- source: { type: "server", id: "https://shop.example.com", previous_id: "" }
119
+ source: { type: "express", platform: "server" }
117
120
  }),
118
121
  mapping: {
119
122
  settings: {
@@ -145,7 +148,7 @@ var purchaseWithTwclid = {
145
148
  },
146
149
  { twclid: "23opevjt88psuo13lu8d020qkn" }
147
150
  ],
148
- conversion_id: "1700000902000-gr0up-1",
151
+ conversion_id: "ev-1700000902000",
149
152
  value: "89.99"
150
153
  }
151
154
  ]
package/dist/index.js CHANGED
@@ -1 +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
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=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=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=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=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
package/dist/index.js.map CHANGED
@@ -1 +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"]}
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 CHANGED
@@ -1 +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
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=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=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=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=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
@@ -1 +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":[]}
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":[]}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$meta": {
3
3
  "package": "@walkeros/server-destination-twitter",
4
- "version": "3.4.1",
4
+ "version": "4.0.0-next-1777463920154",
5
5
  "type": "destination",
6
6
  "platform": [
7
7
  "server"
@@ -166,42 +166,28 @@
166
166
  "entity": "child",
167
167
  "data": {
168
168
  "is": "subordinated"
169
- },
170
- "nested": [],
171
- "context": {
172
- "element": [
173
- "child",
174
- 0
175
- ]
176
169
  }
177
170
  }
178
171
  ],
179
172
  "consent": {
180
173
  "functional": true
181
174
  },
182
- "id": "1700000901000-gr0up-1",
175
+ "id": "ev-1700000901000",
183
176
  "trigger": "test",
184
177
  "entity": "form",
185
178
  "action": "submit",
186
179
  "timestamp": 1700000901000,
187
180
  "timing": 3.14,
188
- "group": "gr0up",
189
- "count": 1,
190
- "version": {
191
- "source": "3.4.1",
192
- "tagging": 1
193
- },
194
181
  "source": {
195
- "type": "server",
196
- "id": "https://example.com",
197
- "previous_id": ""
182
+ "type": "express",
183
+ "platform": "server"
198
184
  }
199
185
  },
200
186
  "out": [
201
187
  [
202
188
  "sendServer",
203
189
  "https://ads-api.x.com/12/measurement/conversions/o8z6j",
204
- "{\"conversions\":[{\"conversion_time\":\"2023-11-14T22:28:21.000Z\",\"event_id\":\"tw-o8z6j-o8z21\",\"identifiers\":[{\"hashed_email\":\"b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514\"}],\"conversion_id\":\"1700000901000-gr0up-1\"}]}",
190
+ "{\"conversions\":[{\"conversion_time\":\"2023-11-14T22:28:21.000Z\",\"event_id\":\"tw-o8z6j-o8z21\",\"identifiers\":[{\"hashed_email\":\"b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514\"}],\"conversion_id\":\"ev-1700000901000\"}]}",
205
191
  {
206
192
  "headers": {
207
193
  "Authorization": "<OAUTH_SIGNATURE>",
@@ -286,22 +272,15 @@
286
272
  "consent": {
287
273
  "functional": true
288
274
  },
289
- "id": "1700000900000-gr0up-1",
275
+ "id": "ev-1700000900000",
290
276
  "trigger": "load",
291
277
  "entity": "order",
292
278
  "action": "complete",
293
279
  "timestamp": 1700000900000,
294
280
  "timing": 3.14,
295
- "group": "gr0up",
296
- "count": 1,
297
- "version": {
298
- "source": "3.4.1",
299
- "tagging": 1
300
- },
301
281
  "source": {
302
- "type": "server",
303
- "id": "https://shop.example.com",
304
- "previous_id": ""
282
+ "type": "express",
283
+ "platform": "server"
305
284
  }
306
285
  },
307
286
  "mapping": {
@@ -317,7 +296,7 @@
317
296
  [
318
297
  "sendServer",
319
298
  "https://ads-api.x.com/12/measurement/conversions/o8z6j",
320
- "{\"conversions\":[{\"conversion_time\":\"2023-11-14T22:28:20.000Z\",\"event_id\":\"tw-o8z6j-o8z21\",\"identifiers\":[{\"hashed_email\":\"8c87b489ce35cf2e2f39f80e282cb2e804932a56a213983eeeb428407d43b52d\"}],\"conversion_id\":\"1700000900000-gr0up-1\",\"value\":\"249.99\"}]}",
299
+ "{\"conversions\":[{\"conversion_time\":\"2023-11-14T22:28:20.000Z\",\"event_id\":\"tw-o8z6j-o8z21\",\"identifiers\":[{\"hashed_email\":\"8c87b489ce35cf2e2f39f80e282cb2e804932a56a213983eeeb428407d43b52d\"}],\"conversion_id\":\"ev-1700000900000\",\"value\":\"249.99\"}]}",
321
300
  {
322
301
  "headers": {
323
302
  "Authorization": "<OAUTH_SIGNATURE>",
@@ -402,22 +381,15 @@
402
381
  "consent": {
403
382
  "functional": true
404
383
  },
405
- "id": "1700000902000-gr0up-1",
384
+ "id": "ev-1700000902000",
406
385
  "trigger": "load",
407
386
  "entity": "order",
408
387
  "action": "complete",
409
388
  "timestamp": 1700000902000,
410
389
  "timing": 3.14,
411
- "group": "gr0up",
412
- "count": 1,
413
- "version": {
414
- "source": "3.4.1",
415
- "tagging": 1
416
- },
417
390
  "source": {
418
- "type": "server",
419
- "id": "https://shop.example.com",
420
- "previous_id": ""
391
+ "type": "express",
392
+ "platform": "server"
421
393
  }
422
394
  },
423
395
  "mapping": {
@@ -442,7 +414,7 @@
442
414
  [
443
415
  "sendServer",
444
416
  "https://ads-api.x.com/12/measurement/conversions/o8z6j",
445
- "{\"conversions\":[{\"conversion_time\":\"2023-11-14T22:28:22.000Z\",\"event_id\":\"tw-o8z6j-o8z21\",\"identifiers\":[{\"hashed_email\":\"484c39bfb51212665d9673805c112b5ba04cbf0460b6d3f00bcdc18b92afed66\"},{\"twclid\":\"23opevjt88psuo13lu8d020qkn\"}],\"conversion_id\":\"1700000902000-gr0up-1\",\"value\":\"89.99\"}]}",
417
+ "{\"conversions\":[{\"conversion_time\":\"2023-11-14T22:28:22.000Z\",\"event_id\":\"tw-o8z6j-o8z21\",\"identifiers\":[{\"hashed_email\":\"484c39bfb51212665d9673805c112b5ba04cbf0460b6d3f00bcdc18b92afed66\"},{\"twclid\":\"23opevjt88psuo13lu8d020qkn\"}],\"conversion_id\":\"ev-1700000902000\",\"value\":\"89.99\"}]}",
446
418
  {
447
419
  "headers": {
448
420
  "Authorization": "<OAUTH_SIGNATURE>",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@walkeros/server-destination-twitter",
3
3
  "description": "X (Twitter) Conversions API server destination for walkerOS",
4
- "version": "3.4.1",
4
+ "version": "4.0.0-next-1777463920154",
5
5
  "license": "MIT",
6
6
  "exports": {
7
7
  ".": {
@@ -34,12 +34,12 @@
34
34
  "update": "npx npm-check-updates -u && npm update"
35
35
  },
36
36
  "dependencies": {
37
- "@walkeros/core": "3.4.1",
38
- "@walkeros/server-core": "3.4.1",
37
+ "@walkeros/core": "4.0.0-next-1777463920154",
38
+ "@walkeros/server-core": "4.0.0-next-1777463920154",
39
39
  "oauth-1.0a": "^2.2.6"
40
40
  },
41
41
  "devDependencies": {
42
- "@walkeros/collector": "3.4.1"
42
+ "@walkeros/collector": "4.0.0-next-1777463920154"
43
43
  },
44
44
  "repository": {
45
45
  "url": "git+https://github.com/elbwalker/walkerOS.git",