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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,197 @@
1
+ # @walkeros/server-destination-twitter
2
+
3
+ X (Twitter) Conversions API (CAPI) server destination for walkerOS. Streams
4
+ conversion events to X via server-to-server HTTPS POST, authenticated with OAuth
5
+ 1.0a.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @walkeros/server-destination-twitter
11
+ ```
12
+
13
+ ## Prerequisites
14
+
15
+ - X Developer account with **Ads API access**
16
+ - Project with **AD_MANAGER** or **ACCOUNT_ADMIN** role
17
+ - Pre-registered conversion events in **Ads Manager** (provides the `eventId` in
18
+ `tw-xxxxx-xxxxx` format)
19
+ - OAuth 1.0a credentials (Consumer Key/Secret + Access Token/Secret)
20
+
21
+ ## Minimal Config
22
+
23
+ ```json
24
+ {
25
+ "destinations": {
26
+ "twitter": {
27
+ "package": "@walkeros/server-destination-twitter",
28
+ "config": {
29
+ "consent": { "marketing": true },
30
+ "settings": {
31
+ "pixelId": "o8z6j",
32
+ "eventId": "tw-o8z6j-o8z21",
33
+ "consumerKey": "$env:TWITTER_CONSUMER_KEY",
34
+ "consumerSecret": "$env:TWITTER_CONSUMER_SECRET",
35
+ "accessToken": "$env:TWITTER_ACCESS_TOKEN",
36
+ "accessTokenSecret": "$env:TWITTER_ACCESS_TOKEN_SECRET"
37
+ }
38
+ }
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ ## Full Config
45
+
46
+ ```json
47
+ {
48
+ "destinations": {
49
+ "twitter": {
50
+ "package": "@walkeros/server-destination-twitter",
51
+ "config": {
52
+ "consent": { "marketing": true },
53
+ "settings": {
54
+ "pixelId": "o8z6j",
55
+ "eventId": "tw-o8z6j-o8z21",
56
+ "consumerKey": "$env:TWITTER_CONSUMER_KEY",
57
+ "consumerSecret": "$env:TWITTER_CONSUMER_SECRET",
58
+ "accessToken": "$env:TWITTER_ACCESS_TOKEN",
59
+ "accessTokenSecret": "$env:TWITTER_ACCESS_TOKEN_SECRET",
60
+ "apiVersion": "12",
61
+ "user_data": {
62
+ "email": "user.email",
63
+ "phone": "user.phone",
64
+ "twclid": "context.twclid",
65
+ "ip_address": "context.ip",
66
+ "user_agent": "context.userAgent"
67
+ }
68
+ },
69
+ "mapping": {
70
+ "order": {
71
+ "complete": {
72
+ "settings": {
73
+ "value": "data.total",
74
+ "currency": { "key": "data.currency", "value": "USD" },
75
+ "number_items": "data.count"
76
+ }
77
+ }
78
+ },
79
+ "form": { "submit": {} }
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ ## Settings
88
+
89
+ | Setting | Type | Required | Default | Description |
90
+ | ------------------- | -------- | -------- | -------------------------- | ------------------------------------------ |
91
+ | `pixelId` | string | yes | - | X Pixel ID in the endpoint URL |
92
+ | `eventId` | string | yes | - | Default conversion event ID (`tw-xxx-xxx`) |
93
+ | `consumerKey` | string | yes | - | OAuth 1.0a API Key |
94
+ | `consumerSecret` | string | yes | - | OAuth 1.0a API Key Secret |
95
+ | `accessToken` | string | yes | - | OAuth 1.0a User Access Token |
96
+ | `accessTokenSecret` | string | yes | - | OAuth 1.0a User Access Token Secret |
97
+ | `apiVersion` | string | no | `"12"` | X Ads API version number |
98
+ | `doNotHash` | string[] | no | `[]` | User data fields already hashed upstream |
99
+ | `url` | string | no | `"https://ads-api.x.com/"` | API base URL override (for testing) |
100
+ | `user_data` | object | no | - | Mapping config for user identifiers |
101
+
102
+ ## Mapping Settings
103
+
104
+ Per-event overrides via `mapping.settings`:
105
+
106
+ | Field | Type | Description |
107
+ | -------------- | ------------- | --------------------------------------------------- |
108
+ | `eventId` | string | Override the default conversion `eventId` per event |
109
+ | `value` | string/number | Conversion monetary value (sent as a string to X) |
110
+ | `currency` | string | ISO 4217 currency code |
111
+ | `number_items` | number | Integer number of items in the conversion |
112
+ | `description` | string | Free-text description of the conversion |
113
+
114
+ ## User Identification
115
+
116
+ The destination builds an `identifiers` array. Each identifier is a separate
117
+ single-key object in the payload.
118
+
119
+ Primary identifiers (at least one required):
120
+
121
+ - **`hashed_email`** -- from `event.user.email` or `user_data.email`, SHA-256
122
+ hashed (trimmed + lowercased).
123
+ - **`hashed_phone_number`** -- from `event.user.phone` or `user_data.phone`,
124
+ SHA-256 hashed.
125
+ - **`twclid`** -- X click ID (pass-through, **not** hashed). Typically forwarded
126
+ from the browser via `context.twclid`.
127
+
128
+ Secondary identifiers (optional):
129
+
130
+ - **`ip_address`** -- client IP, pass-through.
131
+ - **`user_agent`** -- user agent string, pass-through.
132
+
133
+ Use `doNotHash: ['email']` or `doNotHash: ['phone']` to skip hashing for values
134
+ that are already SHA-256 hashed upstream.
135
+
136
+ Events without any primary identifier are silently skipped.
137
+
138
+ ## Deduplication
139
+
140
+ The walkerOS event `id` is sent as `conversion_id`. When paired with the
141
+ browser-side X (Twitter) Pixel, use the same ID on both sides so X can
142
+ deduplicate duplicate conversions across web and server signals.
143
+
144
+ ## Multi-Event Setup
145
+
146
+ Use a single destination with per-event `eventId` overrides:
147
+
148
+ ```json
149
+ {
150
+ "destinations": {
151
+ "twitter": {
152
+ "package": "@walkeros/server-destination-twitter",
153
+ "config": {
154
+ "settings": {
155
+ "pixelId": "o8z6j",
156
+ "eventId": "tw-o8z6j-default-001",
157
+ "consumerKey": "$env:TWITTER_CONSUMER_KEY",
158
+ "consumerSecret": "$env:TWITTER_CONSUMER_SECRET",
159
+ "accessToken": "$env:TWITTER_ACCESS_TOKEN",
160
+ "accessTokenSecret": "$env:TWITTER_ACCESS_TOKEN_SECRET"
161
+ },
162
+ "mapping": {
163
+ "order": {
164
+ "complete": {
165
+ "settings": {
166
+ "eventId": { "value": "tw-o8z6j-purchase-01" },
167
+ "value": "data.total",
168
+ "currency": { "key": "data.currency", "value": "USD" }
169
+ }
170
+ }
171
+ },
172
+ "form": {
173
+ "submit": {
174
+ "settings": {
175
+ "eventId": { "value": "tw-o8z6j-lead-01" }
176
+ }
177
+ }
178
+ }
179
+ }
180
+ }
181
+ }
182
+ }
183
+ }
184
+ ```
185
+
186
+ ## Consent
187
+
188
+ Marketing consent is required. Configure via `config.consent`:
189
+
190
+ ```json
191
+ "consent": { "marketing": true }
192
+ ```
193
+
194
+ ## Links
195
+
196
+ - [X Conversions API docs](https://developer.x.com/en/docs/twitter-ads-api/measurement/api-reference/conversions)
197
+ - [walkerOS documentation](https://www.walkeros.io/docs/destinations/server/twitter)
package/dist/dev.d.mts ADDED
@@ -0,0 +1,110 @@
1
+ import * as _walkeros_core_dev from '@walkeros/core/dev';
2
+ import { z } from '@walkeros/core/dev';
3
+ import { DestinationServer, sendServer } from '@walkeros/server-core';
4
+ import { Flow } from '@walkeros/core';
5
+
6
+ /**
7
+ * X (Twitter) Identifier Type Enum
8
+ * Types of user identifiers supported by X Conversions API.
9
+ * Primary: twclid, hashed_email, hashed_phone_number.
10
+ * Secondary: ip_address, user_agent.
11
+ */
12
+ declare const IdentifierTypeSchema: z.ZodEnum<{
13
+ twclid: "twclid";
14
+ hashed_email: "hashed_email";
15
+ hashed_phone_number: "hashed_phone_number";
16
+ ip_address: "ip_address";
17
+ user_agent: "user_agent";
18
+ }>;
19
+ /**
20
+ * X Event ID Schema
21
+ * Pre-registered conversion event IDs follow the pattern `tw-xxxxx-xxxxx`.
22
+ */
23
+ declare const EventIdSchema: z.ZodString;
24
+ /**
25
+ * API Version Schema
26
+ * X Ads API version number (numeric string).
27
+ */
28
+ declare const ApiVersionSchema: z.ZodString;
29
+
30
+ declare const SettingsSchema: z.ZodObject<{
31
+ pixelId: z.ZodString;
32
+ eventId: z.ZodString;
33
+ consumerKey: z.ZodString;
34
+ consumerSecret: z.ZodString;
35
+ accessToken: z.ZodString;
36
+ accessTokenSecret: z.ZodString;
37
+ apiVersion: z.ZodOptional<z.ZodString>;
38
+ doNotHash: z.ZodOptional<z.ZodArray<z.ZodString>>;
39
+ url: z.ZodOptional<z.ZodString>;
40
+ user_data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
41
+ }, z.core.$strip>;
42
+ type Settings = z.infer<typeof SettingsSchema>;
43
+
44
+ /**
45
+ * X (Twitter) Conversions API Mapping Schema
46
+ *
47
+ * Per-event override for the conversion event ID and monetary details.
48
+ */
49
+ declare const MappingSchema: z.ZodObject<{
50
+ eventId: z.ZodOptional<z.ZodString>;
51
+ value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
52
+ currency: z.ZodOptional<z.ZodString>;
53
+ number_items: z.ZodOptional<z.ZodNumber>;
54
+ description: z.ZodOptional<z.ZodString>;
55
+ }, z.core.$strip>;
56
+ type Mapping = z.infer<typeof MappingSchema>;
57
+
58
+ declare const settings: _walkeros_core_dev.JSONSchema;
59
+ declare const mapping: _walkeros_core_dev.JSONSchema;
60
+
61
+ declare const index$1_ApiVersionSchema: typeof ApiVersionSchema;
62
+ declare const index$1_EventIdSchema: typeof EventIdSchema;
63
+ declare const index$1_IdentifierTypeSchema: typeof IdentifierTypeSchema;
64
+ type index$1_Mapping = Mapping;
65
+ declare const index$1_MappingSchema: typeof MappingSchema;
66
+ type index$1_Settings = Settings;
67
+ declare const index$1_SettingsSchema: typeof SettingsSchema;
68
+ declare const index$1_mapping: typeof mapping;
69
+ declare const index$1_settings: typeof settings;
70
+ declare namespace index$1 {
71
+ export { index$1_ApiVersionSchema as ApiVersionSchema, index$1_EventIdSchema as EventIdSchema, index$1_IdentifierTypeSchema as IdentifierTypeSchema, type index$1_Mapping as Mapping, index$1_MappingSchema as MappingSchema, type index$1_Settings as Settings, index$1_SettingsSchema as SettingsSchema, index$1_mapping as mapping, index$1_settings as settings };
72
+ }
73
+
74
+ interface Env extends DestinationServer.Env {
75
+ sendServer?: typeof sendServer;
76
+ }
77
+
78
+ /**
79
+ * Standard mock environment for push operations
80
+ *
81
+ * Use this for testing X Conversions API events without making
82
+ * actual HTTP requests to X's servers.
83
+ */
84
+ declare const push: Env;
85
+ declare const simulation: string[];
86
+
87
+ declare const env_push: typeof push;
88
+ declare const env_simulation: typeof simulation;
89
+ declare namespace env {
90
+ export { env_push as push, env_simulation as simulation };
91
+ }
92
+
93
+ declare const purchase: Flow.StepExample;
94
+ declare const lead: Flow.StepExample;
95
+ declare const purchaseWithTwclid: Flow.StepExample;
96
+
97
+ declare const step_lead: typeof lead;
98
+ declare const step_purchase: typeof purchase;
99
+ declare const step_purchaseWithTwclid: typeof purchaseWithTwclid;
100
+ declare namespace step {
101
+ export { step_lead as lead, step_purchase as purchase, step_purchaseWithTwclid as purchaseWithTwclid };
102
+ }
103
+
104
+ declare const index_env: typeof env;
105
+ declare const index_step: typeof step;
106
+ declare namespace index {
107
+ export { index_env as env, index_step as step };
108
+ }
109
+
110
+ export { index as examples, index$1 as schemas };
package/dist/dev.d.ts ADDED
@@ -0,0 +1,110 @@
1
+ import * as _walkeros_core_dev from '@walkeros/core/dev';
2
+ import { z } from '@walkeros/core/dev';
3
+ import { DestinationServer, sendServer } from '@walkeros/server-core';
4
+ import { Flow } from '@walkeros/core';
5
+
6
+ /**
7
+ * X (Twitter) Identifier Type Enum
8
+ * Types of user identifiers supported by X Conversions API.
9
+ * Primary: twclid, hashed_email, hashed_phone_number.
10
+ * Secondary: ip_address, user_agent.
11
+ */
12
+ declare const IdentifierTypeSchema: z.ZodEnum<{
13
+ twclid: "twclid";
14
+ hashed_email: "hashed_email";
15
+ hashed_phone_number: "hashed_phone_number";
16
+ ip_address: "ip_address";
17
+ user_agent: "user_agent";
18
+ }>;
19
+ /**
20
+ * X Event ID Schema
21
+ * Pre-registered conversion event IDs follow the pattern `tw-xxxxx-xxxxx`.
22
+ */
23
+ declare const EventIdSchema: z.ZodString;
24
+ /**
25
+ * API Version Schema
26
+ * X Ads API version number (numeric string).
27
+ */
28
+ declare const ApiVersionSchema: z.ZodString;
29
+
30
+ declare const SettingsSchema: z.ZodObject<{
31
+ pixelId: z.ZodString;
32
+ eventId: z.ZodString;
33
+ consumerKey: z.ZodString;
34
+ consumerSecret: z.ZodString;
35
+ accessToken: z.ZodString;
36
+ accessTokenSecret: z.ZodString;
37
+ apiVersion: z.ZodOptional<z.ZodString>;
38
+ doNotHash: z.ZodOptional<z.ZodArray<z.ZodString>>;
39
+ url: z.ZodOptional<z.ZodString>;
40
+ user_data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
41
+ }, z.core.$strip>;
42
+ type Settings = z.infer<typeof SettingsSchema>;
43
+
44
+ /**
45
+ * X (Twitter) Conversions API Mapping Schema
46
+ *
47
+ * Per-event override for the conversion event ID and monetary details.
48
+ */
49
+ declare const MappingSchema: z.ZodObject<{
50
+ eventId: z.ZodOptional<z.ZodString>;
51
+ value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
52
+ currency: z.ZodOptional<z.ZodString>;
53
+ number_items: z.ZodOptional<z.ZodNumber>;
54
+ description: z.ZodOptional<z.ZodString>;
55
+ }, z.core.$strip>;
56
+ type Mapping = z.infer<typeof MappingSchema>;
57
+
58
+ declare const settings: _walkeros_core_dev.JSONSchema;
59
+ declare const mapping: _walkeros_core_dev.JSONSchema;
60
+
61
+ declare const index$1_ApiVersionSchema: typeof ApiVersionSchema;
62
+ declare const index$1_EventIdSchema: typeof EventIdSchema;
63
+ declare const index$1_IdentifierTypeSchema: typeof IdentifierTypeSchema;
64
+ type index$1_Mapping = Mapping;
65
+ declare const index$1_MappingSchema: typeof MappingSchema;
66
+ type index$1_Settings = Settings;
67
+ declare const index$1_SettingsSchema: typeof SettingsSchema;
68
+ declare const index$1_mapping: typeof mapping;
69
+ declare const index$1_settings: typeof settings;
70
+ declare namespace index$1 {
71
+ export { index$1_ApiVersionSchema as ApiVersionSchema, index$1_EventIdSchema as EventIdSchema, index$1_IdentifierTypeSchema as IdentifierTypeSchema, type index$1_Mapping as Mapping, index$1_MappingSchema as MappingSchema, type index$1_Settings as Settings, index$1_SettingsSchema as SettingsSchema, index$1_mapping as mapping, index$1_settings as settings };
72
+ }
73
+
74
+ interface Env extends DestinationServer.Env {
75
+ sendServer?: typeof sendServer;
76
+ }
77
+
78
+ /**
79
+ * Standard mock environment for push operations
80
+ *
81
+ * Use this for testing X Conversions API events without making
82
+ * actual HTTP requests to X's servers.
83
+ */
84
+ declare const push: Env;
85
+ declare const simulation: string[];
86
+
87
+ declare const env_push: typeof push;
88
+ declare const env_simulation: typeof simulation;
89
+ declare namespace env {
90
+ export { env_push as push, env_simulation as simulation };
91
+ }
92
+
93
+ declare const purchase: Flow.StepExample;
94
+ declare const lead: Flow.StepExample;
95
+ declare const purchaseWithTwclid: Flow.StepExample;
96
+
97
+ declare const step_lead: typeof lead;
98
+ declare const step_purchase: typeof purchase;
99
+ declare const step_purchaseWithTwclid: typeof purchaseWithTwclid;
100
+ declare namespace step {
101
+ export { step_lead as lead, step_purchase as purchase, step_purchaseWithTwclid as purchaseWithTwclid };
102
+ }
103
+
104
+ declare const index_env: typeof env;
105
+ declare const index_step: typeof step;
106
+ declare namespace index {
107
+ export { index_env as env, index_step as step };
108
+ }
109
+
110
+ export { index as examples, index$1 as schemas };
package/dist/dev.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";var e,r=Object.defineProperty,t=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,s=Object.prototype.hasOwnProperty,n=(e,t)=>{for(var i in t)r(e,i,{get:t[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||r(e,c,{get:()=>n[c],enumerable:!(a=t(n,c))||a.enumerable});return e})(r({},"__esModule",{value:!0}),e));var a={};n(a,{ApiVersionSchema:()=>l,EventIdSchema:()=>p,IdentifierTypeSchema:()=>m,MappingSchema:()=>h,SettingsSchema:()=>v,mapping:()=>f,settings:()=>g});var c=require("@walkeros/core/dev"),d=require("@walkeros/core/dev"),u=require("@walkeros/core/dev"),m=u.z.enum(["twclid","hashed_email","hashed_phone_number","ip_address","user_agent"]),p=u.z.string().regex(/^tw-[a-z0-9]+-[a-z0-9]+$/,"Event ID must match X format (e.g. tw-xxxxx-xxxxx)"),l=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:p.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:l.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()}),b=require("@walkeros/core/dev"),h=b.z.object({eventId:b.z.string().describe("Override the default conversion event ID for this event").optional(),value:b.z.union([b.z.string(),b.z.number()]).describe("Conversion monetary value (sent to X as a string)").optional(),currency:b.z.string().describe("ISO 4217 currency code (like USD, EUR)").optional(),number_items:b.z.number().int().describe("Number of items in the conversion").optional(),description:b.z.string().describe("Free-text description of the conversion").optional()}),g=(0,c.zodToSchema)(v),f=(0,c.zodToSchema)(h),x={};n(x,{env:()=>z,step:()=>S});var z={};n(z,{push:()=>y,simulation:()=>_});var y={sendServer:async()=>({ok:!0,data:{request_id:"mock-request-id"}})},_=["sendServer"],S={};n(S,{lead:()=>O,purchase:()=>I,purchaseWithTwclid:()=>j});var w=require("@walkeros/core"),k="https://ads-api.x.com/12/measurement/conversions/o8z6j",A={headers:{Authorization:"<OAUTH_SIGNATURE>","Content-Type":"application/json"}},I={in:(0,w.getEvent)("order complete",{timestamp:17000009e5,data:{total:249.99,currency:"EUR"},user:{email:"jane@example.com"},source:{type:"server",id:"https://shop.example.com",previous_id:""}}),mapping:{settings:{value:"data.total",currency:{key:"data.currency",value:"EUR"}}},out:[["sendServer",k,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:20.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"8c87b489ce35cf2e2f39f80e282cb2e804932a56a213983eeeb428407d43b52d"}],conversion_id:"1700000900000-gr0up-1",value:"249.99"}]}),A]]},O={in:(0,w.getEvent)("form submit",{timestamp:1700000901e3,user:{email:"user@example.com"},source:{type:"server",id:"https://example.com",previous_id:""}}),mapping:void 0,out:[["sendServer",k,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:21.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514"}],conversion_id:"1700000901000-gr0up-1"}]}),A]]},j={in:(0,w.getEvent)("order complete",{timestamp:1700000902e3,data:{total:89.99,currency:"USD"},user:{email:"buyer@co.com"},context:{twclid:["23opevjt88psuo13lu8d020qkn",0]},source:{type:"server",id:"https://shop.example.com",previous_id:""}}),mapping:{settings:{value:"data.total",currency:{key:"data.currency",value:"USD"}},data:{map:{user_data:{map:{twclid:"context.twclid"}}}}},out:[["sendServer",k,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:22.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"484c39bfb51212665d9673805c112b5ba04cbf0460b6d3f00bcdc18b92afed66"},{twclid:"23opevjt88psuo13lu8d020qkn"}],conversion_id:"1700000902000-gr0up-1",value:"89.99"}]}),A]]};//# sourceMappingURL=dev.js.map
@@ -0,0 +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 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 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 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"]}
package/dist/dev.mjs ADDED
@@ -0,0 +1 @@
1
+ var e=Object.defineProperty,r=(r,i)=>{for(var t in i)e(r,t,{get:i[t],enumerable:!0})},i={};r(i,{ApiVersionSchema:()=>c,EventIdSchema:()=>a,IdentifierTypeSchema:()=>n,MappingSchema:()=>p,SettingsSchema:()=>d,mapping:()=>l,settings:()=>u});import{zodToSchema as t}from"@walkeros/core/dev";import{z as s}from"@walkeros/core/dev";import{z as o}from"@walkeros/core/dev";var n=o.enum(["twclid","hashed_email","hashed_phone_number","ip_address","user_agent"]),a=o.string().regex(/^tw-[a-z0-9]+-[a-z0-9]+$/,"Event ID must match X format (e.g. tw-xxxxx-xxxxx)"),c=o.string().regex(/^\d+$/,'API version must be numeric (e.g. "12")'),d=s.object({pixelId:s.string().min(1).describe("X Pixel ID used in the Conversions API endpoint URL"),eventId:a.describe("Default pre-registered conversion event ID (like tw-xxxxx-xxxxx)"),consumerKey:s.string().min(1).describe("OAuth 1.0a API Key (Consumer Key) for X Ads API"),consumerSecret:s.string().min(1).describe("OAuth 1.0a API Key Secret (Consumer Secret)"),accessToken:s.string().min(1).describe("OAuth 1.0a User Access Token"),accessTokenSecret:s.string().min(1).describe("OAuth 1.0a User Access Token Secret"),apiVersion:c.describe('X Ads API version number (like "12")').optional(),doNotHash:s.array(s.string()).describe("Array of user data fields that should not be hashed (like ['email'])").optional(),url:s.string().url().describe("Custom base URL for the X Conversions API endpoint (like https://ads-api.x.com/)").optional(),user_data:s.record(s.string(),s.string()).describe("Mapping configuration for user identifiers (like { email: 'user.email', twclid: 'context.twclid' })").optional()});import{z as m}from"@walkeros/core/dev";var p=m.object({eventId:m.string().describe("Override the default conversion event ID for this event").optional(),value:m.union([m.string(),m.number()]).describe("Conversion monetary value (sent to X as a string)").optional(),currency:m.string().describe("ISO 4217 currency code (like USD, EUR)").optional(),number_items:m.number().int().describe("Number of items in the conversion").optional(),description:m.string().describe("Free-text description of the conversion").optional()}),u=t(d),l=t(p),v={};r(v,{env:()=>b,step:()=>g});var b={};r(b,{push:()=>h,simulation:()=>f});var h={sendServer:async()=>({ok:!0,data:{request_id:"mock-request-id"}})},f=["sendServer"],g={};r(g,{lead:()=>k,purchase:()=>S,purchaseWithTwclid:()=>A});import{getEvent as x}from"@walkeros/core";var y="https://ads-api.x.com/12/measurement/conversions/o8z6j",_={headers:{Authorization:"<OAUTH_SIGNATURE>","Content-Type":"application/json"}},S={in:x("order complete",{timestamp:17000009e5,data:{total:249.99,currency:"EUR"},user:{email:"jane@example.com"},source:{type:"server",id:"https://shop.example.com",previous_id:""}}),mapping:{settings:{value:"data.total",currency:{key:"data.currency",value:"EUR"}}},out:[["sendServer",y,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:20.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"8c87b489ce35cf2e2f39f80e282cb2e804932a56a213983eeeb428407d43b52d"}],conversion_id:"1700000900000-gr0up-1",value:"249.99"}]}),_]]},k={in:x("form submit",{timestamp:1700000901e3,user:{email:"user@example.com"},source:{type:"server",id:"https://example.com",previous_id:""}}),mapping:void 0,out:[["sendServer",y,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:21.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514"}],conversion_id:"1700000901000-gr0up-1"}]}),_]]},A={in:x("order complete",{timestamp:1700000902e3,data:{total:89.99,currency:"USD"},user:{email:"buyer@co.com"},context:{twclid:["23opevjt88psuo13lu8d020qkn",0]},source:{type:"server",id:"https://shop.example.com",previous_id:""}}),mapping:{settings:{value:"data.total",currency:{key:"data.currency",value:"USD"}},data:{map:{user_data:{map:{twclid:"context.twclid"}}}}},out:[["sendServer",y,JSON.stringify({conversions:[{conversion_time:"2023-11-14T22:28:22.000Z",event_id:"tw-o8z6j-o8z21",identifiers:[{hashed_email:"484c39bfb51212665d9673805c112b5ba04cbf0460b6d3f00bcdc18b92afed66"},{twclid:"23opevjt88psuo13lu8d020qkn"}],conversion_id:"1700000902000-gr0up-1",value:"89.99"}]}),_]]};export{v as examples,i as schemas};//# sourceMappingURL=dev.mjs.map
@@ -0,0 +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 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 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 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"]}
@@ -0,0 +1,34 @@
1
+ import { DestinationServer, sendServer } from '@walkeros/server-core';
2
+ import { Flow } from '@walkeros/core';
3
+
4
+ interface Env extends DestinationServer.Env {
5
+ sendServer?: typeof sendServer;
6
+ }
7
+
8
+ /**
9
+ * Standard mock environment for push operations
10
+ *
11
+ * Use this for testing X Conversions API events without making
12
+ * actual HTTP requests to X's servers.
13
+ */
14
+ declare const push: Env;
15
+ declare const simulation: string[];
16
+
17
+ declare const env_push: typeof push;
18
+ declare const env_simulation: typeof simulation;
19
+ declare namespace env {
20
+ export { env_push as push, env_simulation as simulation };
21
+ }
22
+
23
+ declare const purchase: Flow.StepExample;
24
+ declare const lead: Flow.StepExample;
25
+ declare const purchaseWithTwclid: Flow.StepExample;
26
+
27
+ declare const step_lead: typeof lead;
28
+ declare const step_purchase: typeof purchase;
29
+ declare const step_purchaseWithTwclid: typeof purchaseWithTwclid;
30
+ declare namespace step {
31
+ export { step_lead as lead, step_purchase as purchase, step_purchaseWithTwclid as purchaseWithTwclid };
32
+ }
33
+
34
+ export { env, step };
@@ -0,0 +1,34 @@
1
+ import { DestinationServer, sendServer } from '@walkeros/server-core';
2
+ import { Flow } from '@walkeros/core';
3
+
4
+ interface Env extends DestinationServer.Env {
5
+ sendServer?: typeof sendServer;
6
+ }
7
+
8
+ /**
9
+ * Standard mock environment for push operations
10
+ *
11
+ * Use this for testing X Conversions API events without making
12
+ * actual HTTP requests to X's servers.
13
+ */
14
+ declare const push: Env;
15
+ declare const simulation: string[];
16
+
17
+ declare const env_push: typeof push;
18
+ declare const env_simulation: typeof simulation;
19
+ declare namespace env {
20
+ export { env_push as push, env_simulation as simulation };
21
+ }
22
+
23
+ declare const purchase: Flow.StepExample;
24
+ declare const lead: Flow.StepExample;
25
+ declare const purchaseWithTwclid: Flow.StepExample;
26
+
27
+ declare const step_lead: typeof lead;
28
+ declare const step_purchase: typeof purchase;
29
+ declare const step_purchaseWithTwclid: typeof purchaseWithTwclid;
30
+ declare namespace step {
31
+ export { step_lead as lead, step_purchase as purchase, step_purchaseWithTwclid as purchaseWithTwclid };
32
+ }
33
+
34
+ export { env, step };