@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 +1 -1
- package/dist/dev.js.map +1 -1
- package/dist/dev.mjs +1 -1
- package/dist/dev.mjs.map +1 -1
- package/dist/examples/index.js +9 -6
- package/dist/examples/index.mjs +9 -6
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/walkerOS.json +13 -41
- package/package.json +4 -4
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,
|
|
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":"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","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,
|
|
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":"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","names":["z","z","z"]}
|
package/dist/examples/index.js
CHANGED
|
@@ -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: "
|
|
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
|
|
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: "
|
|
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
|
|
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: "
|
|
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
|
|
172
|
+
conversion_id: "ev-1700000902000",
|
|
170
173
|
value: "89.99"
|
|
171
174
|
}
|
|
172
175
|
]
|
package/dist/examples/index.mjs
CHANGED
|
@@ -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: "
|
|
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
|
|
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: "
|
|
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
|
|
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: "
|
|
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
|
|
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=
|
|
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=
|
|
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
|
package/dist/index.mjs.map
CHANGED
|
@@ -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":[]}
|
package/dist/walkerOS.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$meta": {
|
|
3
3
|
"package": "@walkeros/server-destination-twitter",
|
|
4
|
-
"version": "
|
|
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
|
|
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": "
|
|
196
|
-
"
|
|
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
|
|
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
|
|
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": "
|
|
303
|
-
"
|
|
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
|
|
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
|
|
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": "
|
|
419
|
-
"
|
|
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
|
|
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": "
|
|
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": "
|
|
38
|
-
"@walkeros/server-core": "
|
|
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": "
|
|
42
|
+
"@walkeros/collector": "4.0.0-next-1777463920154"
|
|
43
43
|
},
|
|
44
44
|
"repository": {
|
|
45
45
|
"url": "git+https://github.com/elbwalker/walkerOS.git",
|