@walkeros/server-destination-reddit 4.7.0-next-1790245733116 → 4.7.0

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/CHANGELOG.md CHANGED
@@ -1,24 +1,38 @@
1
1
  # @walkeros/server-destination-reddit
2
2
 
3
- ## 4.7.0-next-1790245733116
3
+ ## 4.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 74821ed: New `ip` and `userAgent` settings on the Meta, Pinterest, Snapchat,
8
+ TikTok, Reddit, X (Twitter), Bing, Criteo and Google Data Manager server
9
+ destinations. Requests now send the client IP and user agent in each vendor's
10
+ field by default, read from `ingest.ip`/`ingest.userAgent` or
11
+ `user.ip`/`user.userAgent`. A mapped value still wins; set either setting to
12
+ `false` to opt out.
4
13
 
5
14
  ### Patch Changes
6
15
 
7
- - Updated dependencies [57788bc]
16
+ - Updated dependencies [74821ed]
8
17
  - Updated dependencies [06b498a]
9
- - Updated dependencies [f5a1cfd]
10
- - Updated dependencies [ef02916]
18
+ - Updated dependencies [74821ed]
19
+ - Updated dependencies [64b06de]
20
+ - Updated dependencies [4b4937f]
21
+ - Updated dependencies [74821ed]
22
+ - Updated dependencies [74821ed]
23
+ - Updated dependencies [74821ed]
11
24
  - Updated dependencies [06b498a]
12
- - Updated dependencies [ef02916]
13
- - Updated dependencies [2aebc53]
14
- - Updated dependencies [f5a1cfd]
15
- - Updated dependencies [f5a1cfd]
16
- - Updated dependencies [2aebc53]
17
- - Updated dependencies [8f8aea6]
18
- - Updated dependencies [2aebc53]
19
- - Updated dependencies [2aebc53]
20
- - @walkeros/core@4.7.0-next-1790245733116
21
- - @walkeros/server-core@4.7.0-next-1790245733116
25
+ - Updated dependencies [74821ed]
26
+ - Updated dependencies [74821ed]
27
+ - Updated dependencies [74821ed]
28
+ - Updated dependencies [74821ed]
29
+ - Updated dependencies [74821ed]
30
+ - Updated dependencies [e860006]
31
+ - Updated dependencies [74821ed]
32
+ - Updated dependencies [74821ed]
33
+ - Updated dependencies [4f89234]
34
+ - @walkeros/core@4.7.0
35
+ - @walkeros/server-core@4.7.0
22
36
 
23
37
  ## 4.6.1
24
38
 
package/dist/dev.d.mts CHANGED
@@ -42,6 +42,8 @@ declare const SettingsSchema: z.ZodObject<{
42
42
  test_mode: z.ZodOptional<z.ZodBoolean>;
43
43
  url: z.ZodOptional<z.ZodString>;
44
44
  user_data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
45
+ ip: z.ZodOptional<z.ZodUnion<readonly [z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodLiteral<false>]>>;
46
+ userAgent: z.ZodOptional<z.ZodUnion<readonly [z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodLiteral<false>]>>;
45
47
  }, z.core.$strip>;
46
48
  type Settings = z.infer<typeof SettingsSchema>;
47
49
 
package/dist/dev.d.ts CHANGED
@@ -42,6 +42,8 @@ declare const SettingsSchema: z.ZodObject<{
42
42
  test_mode: z.ZodOptional<z.ZodBoolean>;
43
43
  url: z.ZodOptional<z.ZodString>;
44
44
  user_data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
45
+ ip: z.ZodOptional<z.ZodUnion<readonly [z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodLiteral<false>]>>;
46
+ userAgent: z.ZodOptional<z.ZodUnion<readonly [z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodLiteral<false>]>>;
45
47
  }, z.core.$strip>;
46
48
  type Settings = z.infer<typeof SettingsSchema>;
47
49
 
package/dist/dev.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e,t=Object.defineProperty,a=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.prototype.hasOwnProperty,n=(e,a)=>{for(var r in a)t(e,r,{get:a[r],enumerable:!0})},s={};n(s,{examples:()=>y,schemas:()=>d}),module.exports=(e=s,((e,n,s,d)=>{if(n&&"object"==typeof n||"function"==typeof n)for(let o of r(n))i.call(e,o)||o===s||t(e,o,{get:()=>n[o],enumerable:!(d=a(n,o))||d.enumerable});return e})(t({},"__esModule",{value:!0}),e));var d={};n(d,{ActionSourceSchema:()=>p,MappingSchema:()=>l,SettingsSchema:()=>v,TrackingTypeSchema:()=>m,mapping:()=>_,settings:()=>g});var o=require("@walkeros/core/dev"),c=require("@walkeros/core/dev"),u=require("@walkeros/core/dev"),p=u.z.enum(["WEBSITE","APP","PHYSICAL_STORE"]),m=u.z.enum(["PageVisit","ViewContent","Search","AddToCart","AddToWishlist","Purchase","Lead","SignUp","Custom"]),v=c.z.object({accessToken:c.z.string().min(1).describe("Reddit Conversion Access Token for Bearer authentication (like rdt_ABC123...)"),pixelId:c.z.string().min(1).describe("Reddit Pixel ID used as the API path parameter (like a2_abcdef123456)"),action_source:p.describe("Source of the event (WEBSITE, APP, PHYSICAL_STORE) (like WEBSITE)").optional(),doNotHash:c.z.array(c.z.string()).describe("Array of user fields that should not be hashed (like ['email'])").optional(),test_mode:c.z.boolean().describe("Enable test mode by sending test_mode: true in the request body (like true)").optional(),url:c.z.string().url().describe("Custom URL for Reddit Conversions API endpoint (like https://ads-api.reddit.com/api/v2.0/conversions/events/)").optional(),user_data:c.z.record(c.z.string(),c.z.string()).describe("Mapping configuration for user fields (like { email: 'user.email', external_id: 'user.id' })").optional()}),l=require("@walkeros/core/dev").z.object({}),g=(0,o.zodToSchema)(v),_=(0,o.zodToSchema)(l),y={};n(y,{env:()=>f,step:()=>S});var f={};n(f,{push:()=>h,simulation:()=>b});var h={sendServer:async()=>({ok:!0,data:{success:!0}})},b=["sendServer"],S={};n(S,{addToCart:()=>E,lead:()=>w,pageVisit:()=>R,purchase:()=>T,search:()=>z,signUp:()=>x});var k=require("@walkeros/core"),A="https://ads-api.reddit.com/api/v2.0/conversions/events/a2_abcdef123456",P={headers:{Authorization:"Bearer s3cr3t"}},T={title:"Purchase",description:"A completed order is sent to the Reddit Conversions API as a Purchase event with value, currency, and items.",in:(0,k.getEvent)("order complete",{id:"ev-1700000900",timestamp:1700000900,data:{id:"ORD-300",total:249.99,currency:"EUR"},nested:[{entity:"product",data:{id:"SKU-A1",name:"Everyday Ruck Snack",category:"bags",price:"129.99",quantity:2}}],user:{id:"user-123",device:"device-456"},source:{type:"express",platform:"server"}}),mapping:{name:"Purchase",data:{map:{event_metadata:{map:{value_decimal:"data.total",currency:{key:"data.currency",value:"EUR"},item_count:{fn:e=>e.nested.filter(e=>"product"===e.entity).length},products:{loop:["nested",{condition:e=>(0,k.isObject)(e)&&"product"===e.entity,map:{id:"data.id",name:"data.name",category:{key:"data.category",value:"uncategorized"}}}]}}}}}},out:[["sendServer",A,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.900Z",event_at_ms:1700000900,event_type:{tracking_type:"Purchase"},user:{},event_metadata:{conversion_id:"ev-1700000900",value_decimal:249.99,currency:"EUR",item_count:1,products:[{id:"SKU-A1",name:"Everyday Ruck Snack",category:"bags"}]}}]}}),P]]},E={title:"Add to cart",description:"A product add is sent to Reddit as an AddToCart conversion with value and product details.",in:(0,k.getEvent)("product add",{id:"ev-1700000901",timestamp:1700000901,data:{id:"SKU-B2",name:"Cool Cap",category:"hats",price:"42.00",quantity:1},user:{id:"user-456"},source:{type:"express",platform:"server"}}),mapping:{name:"AddToCart",data:{map:{event_metadata:{map:{value_decimal:"data.price",currency:{value:"EUR"},item_count:{value:1},products:{set:[{map:{id:"data.id",name:"data.name",category:{key:"data.category",value:"uncategorized"}}}]}}}}}},out:[["sendServer",A,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.901Z",event_at_ms:1700000901,event_type:{tracking_type:"AddToCart"},user:{},event_metadata:{conversion_id:"ev-1700000901",value_decimal:"42.00",currency:"EUR",item_count:1,products:[{id:"SKU-B2",name:"Cool Cap",category:"hats"}]}}]}}),P]]},R={title:"Page visit",description:"A page view is sent to Reddit as a PageVisit conversion used for retargeting audiences.",in:(0,k.getEvent)("page view",{id:"ev-1700000902",timestamp:1700000902,user:{id:"user-789"},source:{type:"express",platform:"server"}}),mapping:{name:"PageVisit"},out:[["sendServer",A,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.902Z",event_at_ms:1700000902,event_type:{tracking_type:"PageVisit"},user:{},event_metadata:{conversion_id:"ev-1700000902"}}]}}),P]]},w={title:"Lead",description:"A form submission is sent to Reddit as a Lead conversion with the SHA-256 hashed email and external id.",in:(0,k.getEvent)("form submit",{id:"ev-1700000903",timestamp:1700000903,data:{form:"contact"},user:{id:"user-lead-1",email:"lead@example.com"},source:{type:"express",platform:"server"}}),mapping:{name:"Lead",data:{map:{user:{map:{email:"user.email",external_id:"user.id"}}}}},out:[["sendServer",A,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.903Z",event_at_ms:1700000903,event_type:{tracking_type:"Lead"},user:{email:"9fbdefe2837a03c9225be80e741f316f4d174d1732b719b6abb6477efc1ae9d2",external_id:"ee818eebb052cf288ffeeb2e09ee35c9946e1a7f53a959cb3ef06d5d4adb78e8"},event_metadata:{conversion_id:"ev-1700000903"}}]}}),P]]},x={title:"Sign up",description:"A user signup is sent to Reddit as a SignUp conversion with hashed user identifiers.",in:(0,k.getEvent)("user signup",{id:"ev-1700000904",timestamp:1700000904,data:{method:"email"},user:{id:"new-user-1",email:"new@example.com"},source:{type:"express",platform:"server"}}),mapping:{name:"SignUp",data:{map:{user:{map:{email:"user.email",external_id:"user.id"}}}}},out:[["sendServer",A,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.904Z",event_at_ms:1700000904,event_type:{tracking_type:"SignUp"},user:{email:"f0030501023327437b06e5c6f87df7871b8e704ae608d1d0b7b24fdd2a06c716",external_id:"b45cf5f6ebc2c6974ea3bd9fab19f8cc3a7cf63054727a9fcd22f1fda97d6dde"},event_metadata:{conversion_id:"ev-1700000904"}}]}}),P]]},z={title:"Search",description:"A site search is sent to Reddit as a Search conversion with an item count in event_metadata.",in:(0,k.getEvent)("site search",{id:"ev-1700000905",timestamp:1700000905,data:{query:"walkerOS destinations"},user:{id:"user-101"},source:{type:"express",platform:"server"}}),mapping:{name:"Search",data:{map:{event_metadata:{map:{item_count:{value:1}}}}}},out:[["sendServer",A,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.905Z",event_at_ms:1700000905,event_type:{tracking_type:"Search"},user:{},event_metadata:{conversion_id:"ev-1700000905",item_count:1}}]}}),P]]};//# sourceMappingURL=dev.js.map
1
+ "use strict";var e,t=Object.defineProperty,a=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,r=Object.prototype.hasOwnProperty,s=(e,a)=>{for(var i in a)t(e,i,{get:a[i],enumerable:!0})},n={};s(n,{examples:()=>y,schemas:()=>d}),module.exports=(e=n,((e,s,n,d)=>{if(s&&"object"==typeof s||"function"==typeof s)for(let o of i(s))r.call(e,o)||o===n||t(e,o,{get:()=>s[o],enumerable:!(d=a(s,o))||d.enumerable});return e})(t({},"__esModule",{value:!0}),e));var d={};s(d,{ActionSourceSchema:()=>p,MappingSchema:()=>g,SettingsSchema:()=>l,TrackingTypeSchema:()=>m,mapping:()=>f,settings:()=>_});var o=require("@walkeros/core/dev"),c=require("@walkeros/core/dev"),u=require("@walkeros/core/dev"),p=u.z.enum(["WEBSITE","APP","PHYSICAL_STORE"]),m=u.z.enum(["PageVisit","ViewContent","Search","AddToCart","AddToWishlist","Purchase","Lead","SignUp","Custom"]),v=c.z.union([c.schemas.MappingSchemas.ValueSchema,c.z.literal(!1)]),l=c.z.object({accessToken:c.z.string().min(1).describe("Reddit Conversion Access Token for Bearer authentication (like rdt_ABC123...)"),pixelId:c.z.string().min(1).describe("Reddit Pixel ID used as the API path parameter (like a2_abcdef123456)"),action_source:p.describe("Source of the event (WEBSITE, APP, PHYSICAL_STORE) (like WEBSITE)").optional(),doNotHash:c.z.array(c.z.string()).describe("Array of user fields that should not be hashed (like ['email'])").optional(),test_mode:c.z.boolean().describe("Enable test mode by sending test_mode: true in the request body (like true)").optional(),url:c.z.string().url().describe("Custom URL for Reddit Conversions API endpoint (like https://ads-api.reddit.com/api/v2.0/conversions/events/)").optional(),user_data:c.z.record(c.z.string(),c.z.string()).describe("Mapping configuration for user fields (like { email: 'user.email', external_id: 'user.id' })").optional(),ip:v.describe('Client IP, sent as user.ip_address (hashed). Resolves against { ingest, event }. Default: ["ingest.ip", "event.user.ip"]. false switches it off. A mapped user.ip_address wins.').optional(),userAgent:v.describe('Client user agent, sent as user.user_agent (hashed). Resolves against { ingest, event }. Default: ["ingest.userAgent", "event.user.userAgent"]. false switches it off. A mapped user.user_agent wins.').optional()}),g=require("@walkeros/core/dev").z.object({}),_=(0,o.zodToSchema)(l),f=(0,o.zodToSchema)(g),y={};s(y,{env:()=>h,step:()=>A});var h={};s(h,{push:()=>b,simulation:()=>S});var b={sendServer:async()=>({ok:!0,data:{success:!0}})},S=["sendServer"],A={};s(A,{addToCart:()=>E,lead:()=>z,pageVisit:()=>R,purchase:()=>T,search:()=>x,signUp:()=>C});var k=require("@walkeros/core"),P="https://ads-api.reddit.com/api/v2.0/conversions/events/a2_abcdef123456",w={headers:{Authorization:"Bearer s3cr3t"}},T={title:"Purchase",description:"A completed order is sent to the Reddit Conversions API as a Purchase event with value, currency, and items.",in:(0,k.getEvent)("order complete",{id:"ev-1700000900",timestamp:1700000900,data:{id:"ORD-300",total:249.99,currency:"EUR"},nested:[{entity:"product",data:{id:"SKU-A1",name:"Everyday Ruck Snack",category:"bags",price:"129.99",quantity:2}}],user:{id:"user-123",device:"device-456"},source:{type:"express",platform:"server"}}),mapping:{name:"Purchase",data:{map:{event_metadata:{map:{value_decimal:"data.total",currency:{key:"data.currency",value:"EUR"},item_count:{fn:e=>e.nested.filter(e=>"product"===e.entity).length},products:{loop:["nested",{condition:e=>(0,k.isObject)(e)&&"product"===e.entity,map:{id:"data.id",name:"data.name",category:{key:"data.category",value:"uncategorized"}}}]}}}}}},out:[["sendServer",P,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.900Z",event_at_ms:1700000900,event_type:{tracking_type:"Purchase"},user:{},event_metadata:{conversion_id:"ev-1700000900",value_decimal:249.99,currency:"EUR",item_count:1,products:[{id:"SKU-A1",name:"Everyday Ruck Snack",category:"bags"}]}}]}}),w]]},E={title:"Add to cart",description:"A product add is sent to Reddit as an AddToCart conversion with value and product details.",in:(0,k.getEvent)("product add",{id:"ev-1700000901",timestamp:1700000901,data:{id:"SKU-B2",name:"Cool Cap",category:"hats",price:"42.00",quantity:1},user:{id:"user-456"},source:{type:"express",platform:"server"}}),mapping:{name:"AddToCart",data:{map:{event_metadata:{map:{value_decimal:"data.price",currency:{value:"EUR"},item_count:{value:1},products:{set:[{map:{id:"data.id",name:"data.name",category:{key:"data.category",value:"uncategorized"}}}]}}}}}},out:[["sendServer",P,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.901Z",event_at_ms:1700000901,event_type:{tracking_type:"AddToCart"},user:{},event_metadata:{conversion_id:"ev-1700000901",value_decimal:"42.00",currency:"EUR",item_count:1,products:[{id:"SKU-B2",name:"Cool Cap",category:"hats"}]}}]}}),w]]},R={title:"Page visit",description:"A page view is sent to Reddit as a PageVisit conversion used for retargeting audiences.",in:(0,k.getEvent)("page view",{id:"ev-1700000902",timestamp:1700000902,user:{id:"user-789"},source:{type:"express",platform:"server"}}),mapping:{name:"PageVisit"},out:[["sendServer",P,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.902Z",event_at_ms:1700000902,event_type:{tracking_type:"PageVisit"},user:{},event_metadata:{conversion_id:"ev-1700000902"}}]}}),w]]},z={title:"Lead",description:"A form submission is sent to Reddit as a Lead conversion with the SHA-256 hashed email and external id.",in:(0,k.getEvent)("form submit",{id:"ev-1700000903",timestamp:1700000903,data:{form:"contact"},user:{id:"user-lead-1",email:"lead@example.com"},source:{type:"express",platform:"server"}}),mapping:{name:"Lead",data:{map:{user:{map:{email:"user.email",external_id:"user.id"}}}}},out:[["sendServer",P,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.903Z",event_at_ms:1700000903,event_type:{tracking_type:"Lead"},user:{email:"9fbdefe2837a03c9225be80e741f316f4d174d1732b719b6abb6477efc1ae9d2",external_id:"ee818eebb052cf288ffeeb2e09ee35c9946e1a7f53a959cb3ef06d5d4adb78e8"},event_metadata:{conversion_id:"ev-1700000903"}}]}}),w]]},C={title:"Sign up",description:"A user signup is sent to Reddit as a SignUp conversion with hashed user identifiers.",in:(0,k.getEvent)("user signup",{id:"ev-1700000904",timestamp:1700000904,data:{method:"email"},user:{id:"new-user-1",email:"new@example.com"},source:{type:"express",platform:"server"}}),mapping:{name:"SignUp",data:{map:{user:{map:{email:"user.email",external_id:"user.id"}}}}},out:[["sendServer",P,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.904Z",event_at_ms:1700000904,event_type:{tracking_type:"SignUp"},user:{email:"f0030501023327437b06e5c6f87df7871b8e704ae608d1d0b7b24fdd2a06c716",external_id:"b45cf5f6ebc2c6974ea3bd9fab19f8cc3a7cf63054727a9fcd22f1fda97d6dde"},event_metadata:{conversion_id:"ev-1700000904"}}]}}),w]]},x={title:"Search",description:"A site search is sent to Reddit as a Search conversion with an item count in event_metadata.",in:(0,k.getEvent)("site search",{id:"ev-1700000905",timestamp:1700000905,data:{query:"walkerOS destinations"},user:{id:"user-101"},source:{type:"express",platform:"server"}}),mapping:{name:"Search",data:{map:{event_metadata:{map:{item_count:{value:1}}}}}},out:[["sendServer",P,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.905Z",event_at_ms:1700000905,event_type:{tracking_type:"Search"},user:{},event_metadata:{conversion_id:"ev-1700000905",item_count:1}}]}}),w]]};//# 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 { ActionSourceSchema } from './primitives';\n\nexport const SettingsSchema = z.object({\n accessToken: z\n .string()\n .min(1)\n .describe(\n 'Reddit Conversion Access Token for Bearer authentication (like rdt_ABC123...)',\n ),\n pixelId: z\n .string()\n .min(1)\n .describe(\n 'Reddit Pixel ID used as the API path parameter (like a2_abcdef123456)',\n ),\n action_source: ActionSourceSchema.describe(\n 'Source of the event (WEBSITE, APP, PHYSICAL_STORE) (like WEBSITE)',\n ).optional(),\n doNotHash: z\n .array(z.string())\n .describe(\"Array of user fields that should not be hashed (like ['email'])\")\n .optional(),\n test_mode: z\n .boolean()\n .describe(\n 'Enable test mode by sending test_mode: true in the request body (like true)',\n )\n .optional(),\n url: z\n .string()\n .url()\n .describe(\n 'Custom URL for Reddit Conversions API endpoint (like https://ads-api.reddit.com/api/v2.0/conversions/events/)',\n )\n .optional(),\n user_data: z\n .record(z.string(), z.string())\n .describe(\n \"Mapping configuration for user fields (like { email: 'user.email', external_id: 'user.id' })\",\n )\n .optional(),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\n/**\n * Action Source Enum\n * Where the conversion event took place\n * https://ads-api.reddit.com/docs/v2/#tag/Conversions-API\n */\nexport const ActionSourceSchema = z.enum(['WEBSITE', 'APP', 'PHYSICAL_STORE']);\n\n/**\n * Tracking Type\n * Standard Reddit Conversions API event types.\n * Custom events use `Custom` with a `custom_event_name`.\n */\nexport const TrackingTypeSchema = z.enum([\n 'PageVisit',\n 'ViewContent',\n 'Search',\n 'AddToCart',\n 'AddToWishlist',\n 'Purchase',\n 'Lead',\n 'SignUp',\n 'Custom',\n]);\n","import { z } from '@walkeros/core/dev';\n\n/**\n * Reddit Conversions API Mapping Schema\n * Reddit CAPI has no event-level mapping configuration\n */\nexport const MappingSchema = z.object({});\n\n/**\n * Type inference from MappingSchema\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 Reddit Conversions API destination\n *\n * These environments provide standardized mock structures for testing\n * and development without requiring actual HTTP requests.\n */\n\ntype MockSendServer = (\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: MockSendServer = async () => {\n // Simulate successful Reddit API response\n return {\n ok: true,\n data: {\n success: true,\n },\n };\n};\n\n/**\n * Standard mock environment for push operations\n *\n * Use this for testing Reddit Conversions API events without making\n * actual HTTP requests to Reddit's servers.\n */\nexport const push: Env = {\n sendServer: mockSendServer,\n};\n\nexport const simulation = ['sendServer'];\n","import type { Flow, WalkerOS } from '@walkeros/core';\nimport { getEvent, isObject } from '@walkeros/core';\n\n/**\n * Reddit Conversions API step examples.\n *\n * At push time, the destination calls\n * `env.sendServer(endpoint, JSON.stringify(body), options)` where\n * `endpoint = ${settings.url}${settings.pixelId}` and\n * `body = { data: { events: [hashedServerEvent] } }`.\n *\n * Test fixture pins `pixelId = 'a2_abcdef123456'` and the default url, so\n * every endpoint resolves to:\n * https://ads-api.reddit.com/api/v2.0/conversions/events/a2_abcdef123456\n *\n * The serverEvent keys are assembled in this order (insertion order matters\n * for `JSON.stringify` string equality):\n * 1. event_at (ISO timestamp)\n * 2. event_at_ms\n * 3. event_type ({ tracking_type, custom_event_name? })\n * 4. ...restEventData (mapped event data minus user/event_metadata/click_id)\n * 5. user (hashed - email, external_id, ip_address, user_agent, idfa, aaid)\n * 6. event_metadata (conversion_id first, then merged metadata)\n * 7. click_id (only when a string is present)\n *\n * `options` carries the Authorization: Bearer <accessToken> header.\n */\nconst ENDPOINT =\n 'https://ads-api.reddit.com/api/v2.0/conversions/events/a2_abcdef123456';\nconst OPTIONS = {\n headers: { Authorization: 'Bearer s3cr3t' },\n};\n\nexport const purchase: Flow.StepExample = {\n title: 'Purchase',\n description:\n 'A completed order is sent to the Reddit Conversions API as a Purchase event with value, currency, and items.',\n in: getEvent('order complete', {\n id: 'ev-1700000900',\n timestamp: 1700000900,\n data: { id: 'ORD-300', total: 249.99, currency: 'EUR' },\n nested: [\n {\n entity: 'product',\n data: {\n id: 'SKU-A1',\n name: 'Everyday Ruck Snack',\n category: 'bags',\n price: '129.99',\n quantity: 2,\n },\n },\n ],\n user: { id: 'user-123', device: 'device-456' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'Purchase',\n data: {\n map: {\n event_metadata: {\n map: {\n value_decimal: 'data.total',\n currency: { key: 'data.currency', value: 'EUR' },\n item_count: {\n fn: (event: unknown) =>\n (event as WalkerOS.Event).nested.filter(\n (item) => item.entity === 'product',\n ).length,\n },\n products: {\n loop: [\n 'nested',\n {\n condition: (entity: unknown) =>\n isObject(entity) && entity.entity === 'product',\n map: {\n id: 'data.id',\n name: 'data.name',\n category: { key: 'data.category', value: 'uncategorized' },\n },\n },\n ],\n },\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.900Z',\n event_at_ms: 1700000900,\n event_type: { tracking_type: 'Purchase' },\n user: {},\n event_metadata: {\n conversion_id: 'ev-1700000900',\n value_decimal: 249.99,\n currency: 'EUR',\n item_count: 1,\n products: [\n {\n id: 'SKU-A1',\n name: 'Everyday Ruck Snack',\n category: 'bags',\n },\n ],\n },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const addToCart: Flow.StepExample = {\n title: 'Add to cart',\n description:\n 'A product add is sent to Reddit as an AddToCart conversion with value and product details.',\n in: getEvent('product add', {\n id: 'ev-1700000901',\n timestamp: 1700000901,\n data: {\n id: 'SKU-B2',\n name: 'Cool Cap',\n category: 'hats',\n price: '42.00',\n quantity: 1,\n },\n user: { id: 'user-456' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'AddToCart',\n data: {\n map: {\n event_metadata: {\n map: {\n value_decimal: 'data.price',\n currency: { value: 'EUR' },\n item_count: { value: 1 },\n products: {\n set: [\n {\n map: {\n id: 'data.id',\n name: 'data.name',\n category: { key: 'data.category', value: 'uncategorized' },\n },\n },\n ],\n },\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.901Z',\n event_at_ms: 1700000901,\n event_type: { tracking_type: 'AddToCart' },\n user: {},\n event_metadata: {\n conversion_id: 'ev-1700000901',\n value_decimal: '42.00',\n currency: 'EUR',\n item_count: 1,\n products: [\n { id: 'SKU-B2', name: 'Cool Cap', category: 'hats' },\n ],\n },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const pageVisit: Flow.StepExample = {\n title: 'Page visit',\n description:\n 'A page view is sent to Reddit as a PageVisit conversion used for retargeting audiences.',\n in: getEvent('page view', {\n id: 'ev-1700000902',\n timestamp: 1700000902,\n user: { id: 'user-789' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'PageVisit',\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.902Z',\n event_at_ms: 1700000902,\n event_type: { tracking_type: 'PageVisit' },\n user: {},\n event_metadata: { conversion_id: 'ev-1700000902' },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const lead: Flow.StepExample = {\n title: 'Lead',\n description:\n 'A form submission is sent to Reddit as a Lead conversion with the SHA-256 hashed email and external id.',\n in: getEvent('form submit', {\n id: 'ev-1700000903',\n timestamp: 1700000903,\n data: { form: 'contact' },\n user: { id: 'user-lead-1', email: 'lead@example.com' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'Lead',\n data: {\n map: {\n user: {\n map: {\n email: 'user.email',\n external_id: 'user.id',\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.903Z',\n event_at_ms: 1700000903,\n event_type: { tracking_type: 'Lead' },\n user: {\n // sha256('lead@example.com')\n email:\n '9fbdefe2837a03c9225be80e741f316f4d174d1732b719b6abb6477efc1ae9d2',\n // sha256('user-lead-1')\n external_id:\n 'ee818eebb052cf288ffeeb2e09ee35c9946e1a7f53a959cb3ef06d5d4adb78e8',\n },\n event_metadata: { conversion_id: 'ev-1700000903' },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const signUp: Flow.StepExample = {\n title: 'Sign up',\n description:\n 'A user signup is sent to Reddit as a SignUp conversion with hashed user identifiers.',\n in: getEvent('user signup', {\n id: 'ev-1700000904',\n timestamp: 1700000904,\n data: { method: 'email' },\n user: { id: 'new-user-1', email: 'new@example.com' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'SignUp',\n data: {\n map: {\n user: {\n map: {\n email: 'user.email',\n external_id: 'user.id',\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.904Z',\n event_at_ms: 1700000904,\n event_type: { tracking_type: 'SignUp' },\n user: {\n // sha256('new@example.com')\n email:\n 'f0030501023327437b06e5c6f87df7871b8e704ae608d1d0b7b24fdd2a06c716',\n // sha256('new-user-1')\n external_id:\n 'b45cf5f6ebc2c6974ea3bd9fab19f8cc3a7cf63054727a9fcd22f1fda97d6dde',\n },\n event_metadata: { conversion_id: 'ev-1700000904' },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const search: Flow.StepExample = {\n title: 'Search',\n description:\n 'A site search is sent to Reddit as a Search conversion with an item count in event_metadata.',\n in: getEvent('site search', {\n id: 'ev-1700000905',\n timestamp: 1700000905,\n data: { query: 'walkerOS destinations' },\n user: { id: 'user-101' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'Search',\n data: {\n map: {\n event_metadata: {\n map: {\n item_count: { value: 1 },\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.905Z',\n event_at_ms: 1700000905,\n event_type: { tracking_type: 'Search' },\n user: {},\n event_metadata: {\n conversion_id: 'ev-1700000905',\n item_count: 1,\n },\n },\n ],\n },\n }),\n OPTIONS,\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 { schemas, z } from '@walkeros/core/dev';\nimport { ActionSourceSchema } from './primitives';\n\nconst InputSchema = z.union([\n schemas.MappingSchemas.ValueSchema,\n z.literal(false),\n]);\n\nexport const SettingsSchema = z.object({\n accessToken: z\n .string()\n .min(1)\n .describe(\n 'Reddit Conversion Access Token for Bearer authentication (like rdt_ABC123...)',\n ),\n pixelId: z\n .string()\n .min(1)\n .describe(\n 'Reddit Pixel ID used as the API path parameter (like a2_abcdef123456)',\n ),\n action_source: ActionSourceSchema.describe(\n 'Source of the event (WEBSITE, APP, PHYSICAL_STORE) (like WEBSITE)',\n ).optional(),\n doNotHash: z\n .array(z.string())\n .describe(\"Array of user fields that should not be hashed (like ['email'])\")\n .optional(),\n test_mode: z\n .boolean()\n .describe(\n 'Enable test mode by sending test_mode: true in the request body (like true)',\n )\n .optional(),\n url: z\n .string()\n .url()\n .describe(\n 'Custom URL for Reddit Conversions API endpoint (like https://ads-api.reddit.com/api/v2.0/conversions/events/)',\n )\n .optional(),\n user_data: z\n .record(z.string(), z.string())\n .describe(\n \"Mapping configuration for user fields (like { email: 'user.email', external_id: 'user.id' })\",\n )\n .optional(),\n ip: InputSchema.describe(\n 'Client IP, sent as user.ip_address (hashed). Resolves against { ingest, event }. Default: [\"ingest.ip\", \"event.user.ip\"]. false switches it off. A mapped user.ip_address wins.',\n ).optional(),\n userAgent: InputSchema.describe(\n 'Client user agent, sent as user.user_agent (hashed). Resolves against { ingest, event }. Default: [\"ingest.userAgent\", \"event.user.userAgent\"]. false switches it off. A mapped user.user_agent wins.',\n ).optional(),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\n/**\n * Action Source Enum\n * Where the conversion event took place\n * https://ads-api.reddit.com/docs/v2/#tag/Conversions-API\n */\nexport const ActionSourceSchema = z.enum(['WEBSITE', 'APP', 'PHYSICAL_STORE']);\n\n/**\n * Tracking Type\n * Standard Reddit Conversions API event types.\n * Custom events use `Custom` with a `custom_event_name`.\n */\nexport const TrackingTypeSchema = z.enum([\n 'PageVisit',\n 'ViewContent',\n 'Search',\n 'AddToCart',\n 'AddToWishlist',\n 'Purchase',\n 'Lead',\n 'SignUp',\n 'Custom',\n]);\n","import { z } from '@walkeros/core/dev';\n\n/**\n * Reddit Conversions API Mapping Schema\n * Reddit CAPI has no event-level mapping configuration\n */\nexport const MappingSchema = z.object({});\n\n/**\n * Type inference from MappingSchema\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 Reddit Conversions API destination\n *\n * These environments provide standardized mock structures for testing\n * and development without requiring actual HTTP requests.\n */\n\ntype MockSendServer = (\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: MockSendServer = async () => {\n // Simulate successful Reddit API response\n return {\n ok: true,\n data: {\n success: true,\n },\n };\n};\n\n/**\n * Standard mock environment for push operations\n *\n * Use this for testing Reddit Conversions API events without making\n * actual HTTP requests to Reddit's servers.\n */\nexport const push: Env = {\n sendServer: mockSendServer,\n};\n\nexport const simulation = ['sendServer'];\n","import type { Flow, WalkerOS } from '@walkeros/core';\nimport { getEvent, isObject } from '@walkeros/core';\n\n/**\n * Reddit Conversions API step examples.\n *\n * At push time, the destination calls\n * `env.sendServer(endpoint, JSON.stringify(body), options)` where\n * `endpoint = ${settings.url}${settings.pixelId}` and\n * `body = { data: { events: [hashedServerEvent] } }`.\n *\n * Test fixture pins `pixelId = 'a2_abcdef123456'` and the default url, so\n * every endpoint resolves to:\n * https://ads-api.reddit.com/api/v2.0/conversions/events/a2_abcdef123456\n *\n * The serverEvent keys are assembled in this order (insertion order matters\n * for `JSON.stringify` string equality):\n * 1. event_at (ISO timestamp)\n * 2. event_at_ms\n * 3. event_type ({ tracking_type, custom_event_name? })\n * 4. ...restEventData (mapped event data minus user/event_metadata/click_id)\n * 5. user (hashed - email, external_id, ip_address, user_agent, idfa, aaid)\n * 6. event_metadata (conversion_id first, then merged metadata)\n * 7. click_id (only when a string is present)\n *\n * `options` carries the Authorization: Bearer <accessToken> header.\n */\nconst ENDPOINT =\n 'https://ads-api.reddit.com/api/v2.0/conversions/events/a2_abcdef123456';\nconst OPTIONS = {\n headers: { Authorization: 'Bearer s3cr3t' },\n};\n\nexport const purchase: Flow.StepExample = {\n title: 'Purchase',\n description:\n 'A completed order is sent to the Reddit Conversions API as a Purchase event with value, currency, and items.',\n in: getEvent('order complete', {\n id: 'ev-1700000900',\n timestamp: 1700000900,\n data: { id: 'ORD-300', total: 249.99, currency: 'EUR' },\n nested: [\n {\n entity: 'product',\n data: {\n id: 'SKU-A1',\n name: 'Everyday Ruck Snack',\n category: 'bags',\n price: '129.99',\n quantity: 2,\n },\n },\n ],\n user: { id: 'user-123', device: 'device-456' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'Purchase',\n data: {\n map: {\n event_metadata: {\n map: {\n value_decimal: 'data.total',\n currency: { key: 'data.currency', value: 'EUR' },\n item_count: {\n fn: (event: unknown) =>\n (event as WalkerOS.Event).nested.filter(\n (item) => item.entity === 'product',\n ).length,\n },\n products: {\n loop: [\n 'nested',\n {\n condition: (entity: unknown) =>\n isObject(entity) && entity.entity === 'product',\n map: {\n id: 'data.id',\n name: 'data.name',\n category: { key: 'data.category', value: 'uncategorized' },\n },\n },\n ],\n },\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.900Z',\n event_at_ms: 1700000900,\n event_type: { tracking_type: 'Purchase' },\n user: {},\n event_metadata: {\n conversion_id: 'ev-1700000900',\n value_decimal: 249.99,\n currency: 'EUR',\n item_count: 1,\n products: [\n {\n id: 'SKU-A1',\n name: 'Everyday Ruck Snack',\n category: 'bags',\n },\n ],\n },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const addToCart: Flow.StepExample = {\n title: 'Add to cart',\n description:\n 'A product add is sent to Reddit as an AddToCart conversion with value and product details.',\n in: getEvent('product add', {\n id: 'ev-1700000901',\n timestamp: 1700000901,\n data: {\n id: 'SKU-B2',\n name: 'Cool Cap',\n category: 'hats',\n price: '42.00',\n quantity: 1,\n },\n user: { id: 'user-456' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'AddToCart',\n data: {\n map: {\n event_metadata: {\n map: {\n value_decimal: 'data.price',\n currency: { value: 'EUR' },\n item_count: { value: 1 },\n products: {\n set: [\n {\n map: {\n id: 'data.id',\n name: 'data.name',\n category: { key: 'data.category', value: 'uncategorized' },\n },\n },\n ],\n },\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.901Z',\n event_at_ms: 1700000901,\n event_type: { tracking_type: 'AddToCart' },\n user: {},\n event_metadata: {\n conversion_id: 'ev-1700000901',\n value_decimal: '42.00',\n currency: 'EUR',\n item_count: 1,\n products: [\n { id: 'SKU-B2', name: 'Cool Cap', category: 'hats' },\n ],\n },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const pageVisit: Flow.StepExample = {\n title: 'Page visit',\n description:\n 'A page view is sent to Reddit as a PageVisit conversion used for retargeting audiences.',\n in: getEvent('page view', {\n id: 'ev-1700000902',\n timestamp: 1700000902,\n user: { id: 'user-789' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'PageVisit',\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.902Z',\n event_at_ms: 1700000902,\n event_type: { tracking_type: 'PageVisit' },\n user: {},\n event_metadata: { conversion_id: 'ev-1700000902' },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const lead: Flow.StepExample = {\n title: 'Lead',\n description:\n 'A form submission is sent to Reddit as a Lead conversion with the SHA-256 hashed email and external id.',\n in: getEvent('form submit', {\n id: 'ev-1700000903',\n timestamp: 1700000903,\n data: { form: 'contact' },\n user: { id: 'user-lead-1', email: 'lead@example.com' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'Lead',\n data: {\n map: {\n user: {\n map: {\n email: 'user.email',\n external_id: 'user.id',\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.903Z',\n event_at_ms: 1700000903,\n event_type: { tracking_type: 'Lead' },\n user: {\n // sha256('lead@example.com')\n email:\n '9fbdefe2837a03c9225be80e741f316f4d174d1732b719b6abb6477efc1ae9d2',\n // sha256('user-lead-1')\n external_id:\n 'ee818eebb052cf288ffeeb2e09ee35c9946e1a7f53a959cb3ef06d5d4adb78e8',\n },\n event_metadata: { conversion_id: 'ev-1700000903' },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const signUp: Flow.StepExample = {\n title: 'Sign up',\n description:\n 'A user signup is sent to Reddit as a SignUp conversion with hashed user identifiers.',\n in: getEvent('user signup', {\n id: 'ev-1700000904',\n timestamp: 1700000904,\n data: { method: 'email' },\n user: { id: 'new-user-1', email: 'new@example.com' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'SignUp',\n data: {\n map: {\n user: {\n map: {\n email: 'user.email',\n external_id: 'user.id',\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.904Z',\n event_at_ms: 1700000904,\n event_type: { tracking_type: 'SignUp' },\n user: {\n // sha256('new@example.com')\n email:\n 'f0030501023327437b06e5c6f87df7871b8e704ae608d1d0b7b24fdd2a06c716',\n // sha256('new-user-1')\n external_id:\n 'b45cf5f6ebc2c6974ea3bd9fab19f8cc3a7cf63054727a9fcd22f1fda97d6dde',\n },\n event_metadata: { conversion_id: 'ev-1700000904' },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const search: Flow.StepExample = {\n title: 'Search',\n description:\n 'A site search is sent to Reddit as a Search conversion with an item count in event_metadata.',\n in: getEvent('site search', {\n id: 'ev-1700000905',\n timestamp: 1700000905,\n data: { query: 'walkerOS destinations' },\n user: { id: 'user-101' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'Search',\n data: {\n map: {\n event_metadata: {\n map: {\n item_count: { value: 1 },\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.905Z',\n event_at_ms: 1700000905,\n event_type: { tracking_type: 'Search' },\n user: {},\n event_metadata: {\n conversion_id: 'ev-1700000905',\n item_count: 1,\n },\n },\n ],\n },\n }),\n OPTIONS,\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,t=(t,a)=>{for(var i in a)e(t,i,{get:a[i],enumerable:!0})},a={};t(a,{ActionSourceSchema:()=>n,MappingSchema:()=>m,SettingsSchema:()=>o,TrackingTypeSchema:()=>d,mapping:()=>u,settings:()=>p});import{zodToSchema as i}from"@walkeros/core/dev";import{z as r}from"@walkeros/core/dev";import{z as s}from"@walkeros/core/dev";var n=s.enum(["WEBSITE","APP","PHYSICAL_STORE"]),d=s.enum(["PageVisit","ViewContent","Search","AddToCart","AddToWishlist","Purchase","Lead","SignUp","Custom"]),o=r.object({accessToken:r.string().min(1).describe("Reddit Conversion Access Token for Bearer authentication (like rdt_ABC123...)"),pixelId:r.string().min(1).describe("Reddit Pixel ID used as the API path parameter (like a2_abcdef123456)"),action_source:n.describe("Source of the event (WEBSITE, APP, PHYSICAL_STORE) (like WEBSITE)").optional(),doNotHash:r.array(r.string()).describe("Array of user fields that should not be hashed (like ['email'])").optional(),test_mode:r.boolean().describe("Enable test mode by sending test_mode: true in the request body (like true)").optional(),url:r.string().url().describe("Custom URL for Reddit Conversions API endpoint (like https://ads-api.reddit.com/api/v2.0/conversions/events/)").optional(),user_data:r.record(r.string(),r.string()).describe("Mapping configuration for user fields (like { email: 'user.email', external_id: 'user.id' })").optional()});import{z as c}from"@walkeros/core/dev";var m=c.object({}),p=i(o),u=i(m),v={};t(v,{env:()=>l,step:()=>y});var l={};t(l,{push:()=>_,simulation:()=>g});var _={sendServer:async()=>({ok:!0,data:{success:!0}})},g=["sendServer"],y={};t(y,{addToCart:()=>A,lead:()=>T,pageVisit:()=>P,purchase:()=>k,search:()=>x,signUp:()=>R});import{getEvent as f,isObject as h}from"@walkeros/core";var S="https://ads-api.reddit.com/api/v2.0/conversions/events/a2_abcdef123456",b={headers:{Authorization:"Bearer s3cr3t"}},k={title:"Purchase",description:"A completed order is sent to the Reddit Conversions API as a Purchase event with value, currency, and items.",in:f("order complete",{id:"ev-1700000900",timestamp:1700000900,data:{id:"ORD-300",total:249.99,currency:"EUR"},nested:[{entity:"product",data:{id:"SKU-A1",name:"Everyday Ruck Snack",category:"bags",price:"129.99",quantity:2}}],user:{id:"user-123",device:"device-456"},source:{type:"express",platform:"server"}}),mapping:{name:"Purchase",data:{map:{event_metadata:{map:{value_decimal:"data.total",currency:{key:"data.currency",value:"EUR"},item_count:{fn:e=>e.nested.filter(e=>"product"===e.entity).length},products:{loop:["nested",{condition:e=>h(e)&&"product"===e.entity,map:{id:"data.id",name:"data.name",category:{key:"data.category",value:"uncategorized"}}}]}}}}}},out:[["sendServer",S,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.900Z",event_at_ms:1700000900,event_type:{tracking_type:"Purchase"},user:{},event_metadata:{conversion_id:"ev-1700000900",value_decimal:249.99,currency:"EUR",item_count:1,products:[{id:"SKU-A1",name:"Everyday Ruck Snack",category:"bags"}]}}]}}),b]]},A={title:"Add to cart",description:"A product add is sent to Reddit as an AddToCart conversion with value and product details.",in:f("product add",{id:"ev-1700000901",timestamp:1700000901,data:{id:"SKU-B2",name:"Cool Cap",category:"hats",price:"42.00",quantity:1},user:{id:"user-456"},source:{type:"express",platform:"server"}}),mapping:{name:"AddToCart",data:{map:{event_metadata:{map:{value_decimal:"data.price",currency:{value:"EUR"},item_count:{value:1},products:{set:[{map:{id:"data.id",name:"data.name",category:{key:"data.category",value:"uncategorized"}}}]}}}}}},out:[["sendServer",S,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.901Z",event_at_ms:1700000901,event_type:{tracking_type:"AddToCart"},user:{},event_metadata:{conversion_id:"ev-1700000901",value_decimal:"42.00",currency:"EUR",item_count:1,products:[{id:"SKU-B2",name:"Cool Cap",category:"hats"}]}}]}}),b]]},P={title:"Page visit",description:"A page view is sent to Reddit as a PageVisit conversion used for retargeting audiences.",in:f("page view",{id:"ev-1700000902",timestamp:1700000902,user:{id:"user-789"},source:{type:"express",platform:"server"}}),mapping:{name:"PageVisit"},out:[["sendServer",S,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.902Z",event_at_ms:1700000902,event_type:{tracking_type:"PageVisit"},user:{},event_metadata:{conversion_id:"ev-1700000902"}}]}}),b]]},T={title:"Lead",description:"A form submission is sent to Reddit as a Lead conversion with the SHA-256 hashed email and external id.",in:f("form submit",{id:"ev-1700000903",timestamp:1700000903,data:{form:"contact"},user:{id:"user-lead-1",email:"lead@example.com"},source:{type:"express",platform:"server"}}),mapping:{name:"Lead",data:{map:{user:{map:{email:"user.email",external_id:"user.id"}}}}},out:[["sendServer",S,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.903Z",event_at_ms:1700000903,event_type:{tracking_type:"Lead"},user:{email:"9fbdefe2837a03c9225be80e741f316f4d174d1732b719b6abb6477efc1ae9d2",external_id:"ee818eebb052cf288ffeeb2e09ee35c9946e1a7f53a959cb3ef06d5d4adb78e8"},event_metadata:{conversion_id:"ev-1700000903"}}]}}),b]]},R={title:"Sign up",description:"A user signup is sent to Reddit as a SignUp conversion with hashed user identifiers.",in:f("user signup",{id:"ev-1700000904",timestamp:1700000904,data:{method:"email"},user:{id:"new-user-1",email:"new@example.com"},source:{type:"express",platform:"server"}}),mapping:{name:"SignUp",data:{map:{user:{map:{email:"user.email",external_id:"user.id"}}}}},out:[["sendServer",S,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.904Z",event_at_ms:1700000904,event_type:{tracking_type:"SignUp"},user:{email:"f0030501023327437b06e5c6f87df7871b8e704ae608d1d0b7b24fdd2a06c716",external_id:"b45cf5f6ebc2c6974ea3bd9fab19f8cc3a7cf63054727a9fcd22f1fda97d6dde"},event_metadata:{conversion_id:"ev-1700000904"}}]}}),b]]},x={title:"Search",description:"A site search is sent to Reddit as a Search conversion with an item count in event_metadata.",in:f("site search",{id:"ev-1700000905",timestamp:1700000905,data:{query:"walkerOS destinations"},user:{id:"user-101"},source:{type:"express",platform:"server"}}),mapping:{name:"Search",data:{map:{event_metadata:{map:{item_count:{value:1}}}}}},out:[["sendServer",S,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.905Z",event_at_ms:1700000905,event_type:{tracking_type:"Search"},user:{},event_metadata:{conversion_id:"ev-1700000905",item_count:1}}]}}),b]]};export{v as examples,a as schemas};//# sourceMappingURL=dev.mjs.map
1
+ var e=Object.defineProperty,t=(t,a)=>{for(var i in a)e(t,i,{get:a[i],enumerable:!0})},a={};t(a,{ActionSourceSchema:()=>d,MappingSchema:()=>u,SettingsSchema:()=>p,TrackingTypeSchema:()=>o,mapping:()=>l,settings:()=>v});import{zodToSchema as i}from"@walkeros/core/dev";import{schemas as r,z as s}from"@walkeros/core/dev";import{z as n}from"@walkeros/core/dev";var d=n.enum(["WEBSITE","APP","PHYSICAL_STORE"]),o=n.enum(["PageVisit","ViewContent","Search","AddToCart","AddToWishlist","Purchase","Lead","SignUp","Custom"]),c=s.union([r.MappingSchemas.ValueSchema,s.literal(!1)]),p=s.object({accessToken:s.string().min(1).describe("Reddit Conversion Access Token for Bearer authentication (like rdt_ABC123...)"),pixelId:s.string().min(1).describe("Reddit Pixel ID used as the API path parameter (like a2_abcdef123456)"),action_source:d.describe("Source of the event (WEBSITE, APP, PHYSICAL_STORE) (like WEBSITE)").optional(),doNotHash:s.array(s.string()).describe("Array of user fields that should not be hashed (like ['email'])").optional(),test_mode:s.boolean().describe("Enable test mode by sending test_mode: true in the request body (like true)").optional(),url:s.string().url().describe("Custom URL for Reddit Conversions API endpoint (like https://ads-api.reddit.com/api/v2.0/conversions/events/)").optional(),user_data:s.record(s.string(),s.string()).describe("Mapping configuration for user fields (like { email: 'user.email', external_id: 'user.id' })").optional(),ip:c.describe('Client IP, sent as user.ip_address (hashed). Resolves against { ingest, event }. Default: ["ingest.ip", "event.user.ip"]. false switches it off. A mapped user.ip_address wins.').optional(),userAgent:c.describe('Client user agent, sent as user.user_agent (hashed). Resolves against { ingest, event }. Default: ["ingest.userAgent", "event.user.userAgent"]. false switches it off. A mapped user.user_agent wins.').optional()});import{z as m}from"@walkeros/core/dev";var u=m.object({}),v=i(p),l=i(u),g={};t(g,{env:()=>_,step:()=>h});var _={};t(_,{push:()=>f,simulation:()=>y});var f={sendServer:async()=>({ok:!0,data:{success:!0}})},y=["sendServer"],h={};t(h,{addToCart:()=>R,lead:()=>w,pageVisit:()=>T,purchase:()=>P,search:()=>x,signUp:()=>C});import{getEvent as S,isObject as b}from"@walkeros/core";var A="https://ads-api.reddit.com/api/v2.0/conversions/events/a2_abcdef123456",k={headers:{Authorization:"Bearer s3cr3t"}},P={title:"Purchase",description:"A completed order is sent to the Reddit Conversions API as a Purchase event with value, currency, and items.",in:S("order complete",{id:"ev-1700000900",timestamp:1700000900,data:{id:"ORD-300",total:249.99,currency:"EUR"},nested:[{entity:"product",data:{id:"SKU-A1",name:"Everyday Ruck Snack",category:"bags",price:"129.99",quantity:2}}],user:{id:"user-123",device:"device-456"},source:{type:"express",platform:"server"}}),mapping:{name:"Purchase",data:{map:{event_metadata:{map:{value_decimal:"data.total",currency:{key:"data.currency",value:"EUR"},item_count:{fn:e=>e.nested.filter(e=>"product"===e.entity).length},products:{loop:["nested",{condition:e=>b(e)&&"product"===e.entity,map:{id:"data.id",name:"data.name",category:{key:"data.category",value:"uncategorized"}}}]}}}}}},out:[["sendServer",A,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.900Z",event_at_ms:1700000900,event_type:{tracking_type:"Purchase"},user:{},event_metadata:{conversion_id:"ev-1700000900",value_decimal:249.99,currency:"EUR",item_count:1,products:[{id:"SKU-A1",name:"Everyday Ruck Snack",category:"bags"}]}}]}}),k]]},R={title:"Add to cart",description:"A product add is sent to Reddit as an AddToCart conversion with value and product details.",in:S("product add",{id:"ev-1700000901",timestamp:1700000901,data:{id:"SKU-B2",name:"Cool Cap",category:"hats",price:"42.00",quantity:1},user:{id:"user-456"},source:{type:"express",platform:"server"}}),mapping:{name:"AddToCart",data:{map:{event_metadata:{map:{value_decimal:"data.price",currency:{value:"EUR"},item_count:{value:1},products:{set:[{map:{id:"data.id",name:"data.name",category:{key:"data.category",value:"uncategorized"}}}]}}}}}},out:[["sendServer",A,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.901Z",event_at_ms:1700000901,event_type:{tracking_type:"AddToCart"},user:{},event_metadata:{conversion_id:"ev-1700000901",value_decimal:"42.00",currency:"EUR",item_count:1,products:[{id:"SKU-B2",name:"Cool Cap",category:"hats"}]}}]}}),k]]},T={title:"Page visit",description:"A page view is sent to Reddit as a PageVisit conversion used for retargeting audiences.",in:S("page view",{id:"ev-1700000902",timestamp:1700000902,user:{id:"user-789"},source:{type:"express",platform:"server"}}),mapping:{name:"PageVisit"},out:[["sendServer",A,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.902Z",event_at_ms:1700000902,event_type:{tracking_type:"PageVisit"},user:{},event_metadata:{conversion_id:"ev-1700000902"}}]}}),k]]},w={title:"Lead",description:"A form submission is sent to Reddit as a Lead conversion with the SHA-256 hashed email and external id.",in:S("form submit",{id:"ev-1700000903",timestamp:1700000903,data:{form:"contact"},user:{id:"user-lead-1",email:"lead@example.com"},source:{type:"express",platform:"server"}}),mapping:{name:"Lead",data:{map:{user:{map:{email:"user.email",external_id:"user.id"}}}}},out:[["sendServer",A,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.903Z",event_at_ms:1700000903,event_type:{tracking_type:"Lead"},user:{email:"9fbdefe2837a03c9225be80e741f316f4d174d1732b719b6abb6477efc1ae9d2",external_id:"ee818eebb052cf288ffeeb2e09ee35c9946e1a7f53a959cb3ef06d5d4adb78e8"},event_metadata:{conversion_id:"ev-1700000903"}}]}}),k]]},C={title:"Sign up",description:"A user signup is sent to Reddit as a SignUp conversion with hashed user identifiers.",in:S("user signup",{id:"ev-1700000904",timestamp:1700000904,data:{method:"email"},user:{id:"new-user-1",email:"new@example.com"},source:{type:"express",platform:"server"}}),mapping:{name:"SignUp",data:{map:{user:{map:{email:"user.email",external_id:"user.id"}}}}},out:[["sendServer",A,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.904Z",event_at_ms:1700000904,event_type:{tracking_type:"SignUp"},user:{email:"f0030501023327437b06e5c6f87df7871b8e704ae608d1d0b7b24fdd2a06c716",external_id:"b45cf5f6ebc2c6974ea3bd9fab19f8cc3a7cf63054727a9fcd22f1fda97d6dde"},event_metadata:{conversion_id:"ev-1700000904"}}]}}),k]]},x={title:"Search",description:"A site search is sent to Reddit as a Search conversion with an item count in event_metadata.",in:S("site search",{id:"ev-1700000905",timestamp:1700000905,data:{query:"walkerOS destinations"},user:{id:"user-101"},source:{type:"express",platform:"server"}}),mapping:{name:"Search",data:{map:{event_metadata:{map:{item_count:{value:1}}}}}},out:[["sendServer",A,JSON.stringify({data:{events:[{event_at:"1970-01-20T16:13:20.905Z",event_at_ms:1700000905,event_type:{tracking_type:"Search"},user:{},event_metadata:{conversion_id:"ev-1700000905",item_count:1}}]}}),k]]};export{g as examples,a 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 { ActionSourceSchema } from './primitives';\n\nexport const SettingsSchema = z.object({\n accessToken: z\n .string()\n .min(1)\n .describe(\n 'Reddit Conversion Access Token for Bearer authentication (like rdt_ABC123...)',\n ),\n pixelId: z\n .string()\n .min(1)\n .describe(\n 'Reddit Pixel ID used as the API path parameter (like a2_abcdef123456)',\n ),\n action_source: ActionSourceSchema.describe(\n 'Source of the event (WEBSITE, APP, PHYSICAL_STORE) (like WEBSITE)',\n ).optional(),\n doNotHash: z\n .array(z.string())\n .describe(\"Array of user fields that should not be hashed (like ['email'])\")\n .optional(),\n test_mode: z\n .boolean()\n .describe(\n 'Enable test mode by sending test_mode: true in the request body (like true)',\n )\n .optional(),\n url: z\n .string()\n .url()\n .describe(\n 'Custom URL for Reddit Conversions API endpoint (like https://ads-api.reddit.com/api/v2.0/conversions/events/)',\n )\n .optional(),\n user_data: z\n .record(z.string(), z.string())\n .describe(\n \"Mapping configuration for user fields (like { email: 'user.email', external_id: 'user.id' })\",\n )\n .optional(),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\n/**\n * Action Source Enum\n * Where the conversion event took place\n * https://ads-api.reddit.com/docs/v2/#tag/Conversions-API\n */\nexport const ActionSourceSchema = z.enum(['WEBSITE', 'APP', 'PHYSICAL_STORE']);\n\n/**\n * Tracking Type\n * Standard Reddit Conversions API event types.\n * Custom events use `Custom` with a `custom_event_name`.\n */\nexport const TrackingTypeSchema = z.enum([\n 'PageVisit',\n 'ViewContent',\n 'Search',\n 'AddToCart',\n 'AddToWishlist',\n 'Purchase',\n 'Lead',\n 'SignUp',\n 'Custom',\n]);\n","import { z } from '@walkeros/core/dev';\n\n/**\n * Reddit Conversions API Mapping Schema\n * Reddit CAPI has no event-level mapping configuration\n */\nexport const MappingSchema = z.object({});\n\n/**\n * Type inference from MappingSchema\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 Reddit Conversions API destination\n *\n * These environments provide standardized mock structures for testing\n * and development without requiring actual HTTP requests.\n */\n\ntype MockSendServer = (\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: MockSendServer = async () => {\n // Simulate successful Reddit API response\n return {\n ok: true,\n data: {\n success: true,\n },\n };\n};\n\n/**\n * Standard mock environment for push operations\n *\n * Use this for testing Reddit Conversions API events without making\n * actual HTTP requests to Reddit's servers.\n */\nexport const push: Env = {\n sendServer: mockSendServer,\n};\n\nexport const simulation = ['sendServer'];\n","import type { Flow, WalkerOS } from '@walkeros/core';\nimport { getEvent, isObject } from '@walkeros/core';\n\n/**\n * Reddit Conversions API step examples.\n *\n * At push time, the destination calls\n * `env.sendServer(endpoint, JSON.stringify(body), options)` where\n * `endpoint = ${settings.url}${settings.pixelId}` and\n * `body = { data: { events: [hashedServerEvent] } }`.\n *\n * Test fixture pins `pixelId = 'a2_abcdef123456'` and the default url, so\n * every endpoint resolves to:\n * https://ads-api.reddit.com/api/v2.0/conversions/events/a2_abcdef123456\n *\n * The serverEvent keys are assembled in this order (insertion order matters\n * for `JSON.stringify` string equality):\n * 1. event_at (ISO timestamp)\n * 2. event_at_ms\n * 3. event_type ({ tracking_type, custom_event_name? })\n * 4. ...restEventData (mapped event data minus user/event_metadata/click_id)\n * 5. user (hashed - email, external_id, ip_address, user_agent, idfa, aaid)\n * 6. event_metadata (conversion_id first, then merged metadata)\n * 7. click_id (only when a string is present)\n *\n * `options` carries the Authorization: Bearer <accessToken> header.\n */\nconst ENDPOINT =\n 'https://ads-api.reddit.com/api/v2.0/conversions/events/a2_abcdef123456';\nconst OPTIONS = {\n headers: { Authorization: 'Bearer s3cr3t' },\n};\n\nexport const purchase: Flow.StepExample = {\n title: 'Purchase',\n description:\n 'A completed order is sent to the Reddit Conversions API as a Purchase event with value, currency, and items.',\n in: getEvent('order complete', {\n id: 'ev-1700000900',\n timestamp: 1700000900,\n data: { id: 'ORD-300', total: 249.99, currency: 'EUR' },\n nested: [\n {\n entity: 'product',\n data: {\n id: 'SKU-A1',\n name: 'Everyday Ruck Snack',\n category: 'bags',\n price: '129.99',\n quantity: 2,\n },\n },\n ],\n user: { id: 'user-123', device: 'device-456' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'Purchase',\n data: {\n map: {\n event_metadata: {\n map: {\n value_decimal: 'data.total',\n currency: { key: 'data.currency', value: 'EUR' },\n item_count: {\n fn: (event: unknown) =>\n (event as WalkerOS.Event).nested.filter(\n (item) => item.entity === 'product',\n ).length,\n },\n products: {\n loop: [\n 'nested',\n {\n condition: (entity: unknown) =>\n isObject(entity) && entity.entity === 'product',\n map: {\n id: 'data.id',\n name: 'data.name',\n category: { key: 'data.category', value: 'uncategorized' },\n },\n },\n ],\n },\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.900Z',\n event_at_ms: 1700000900,\n event_type: { tracking_type: 'Purchase' },\n user: {},\n event_metadata: {\n conversion_id: 'ev-1700000900',\n value_decimal: 249.99,\n currency: 'EUR',\n item_count: 1,\n products: [\n {\n id: 'SKU-A1',\n name: 'Everyday Ruck Snack',\n category: 'bags',\n },\n ],\n },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const addToCart: Flow.StepExample = {\n title: 'Add to cart',\n description:\n 'A product add is sent to Reddit as an AddToCart conversion with value and product details.',\n in: getEvent('product add', {\n id: 'ev-1700000901',\n timestamp: 1700000901,\n data: {\n id: 'SKU-B2',\n name: 'Cool Cap',\n category: 'hats',\n price: '42.00',\n quantity: 1,\n },\n user: { id: 'user-456' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'AddToCart',\n data: {\n map: {\n event_metadata: {\n map: {\n value_decimal: 'data.price',\n currency: { value: 'EUR' },\n item_count: { value: 1 },\n products: {\n set: [\n {\n map: {\n id: 'data.id',\n name: 'data.name',\n category: { key: 'data.category', value: 'uncategorized' },\n },\n },\n ],\n },\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.901Z',\n event_at_ms: 1700000901,\n event_type: { tracking_type: 'AddToCart' },\n user: {},\n event_metadata: {\n conversion_id: 'ev-1700000901',\n value_decimal: '42.00',\n currency: 'EUR',\n item_count: 1,\n products: [\n { id: 'SKU-B2', name: 'Cool Cap', category: 'hats' },\n ],\n },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const pageVisit: Flow.StepExample = {\n title: 'Page visit',\n description:\n 'A page view is sent to Reddit as a PageVisit conversion used for retargeting audiences.',\n in: getEvent('page view', {\n id: 'ev-1700000902',\n timestamp: 1700000902,\n user: { id: 'user-789' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'PageVisit',\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.902Z',\n event_at_ms: 1700000902,\n event_type: { tracking_type: 'PageVisit' },\n user: {},\n event_metadata: { conversion_id: 'ev-1700000902' },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const lead: Flow.StepExample = {\n title: 'Lead',\n description:\n 'A form submission is sent to Reddit as a Lead conversion with the SHA-256 hashed email and external id.',\n in: getEvent('form submit', {\n id: 'ev-1700000903',\n timestamp: 1700000903,\n data: { form: 'contact' },\n user: { id: 'user-lead-1', email: 'lead@example.com' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'Lead',\n data: {\n map: {\n user: {\n map: {\n email: 'user.email',\n external_id: 'user.id',\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.903Z',\n event_at_ms: 1700000903,\n event_type: { tracking_type: 'Lead' },\n user: {\n // sha256('lead@example.com')\n email:\n '9fbdefe2837a03c9225be80e741f316f4d174d1732b719b6abb6477efc1ae9d2',\n // sha256('user-lead-1')\n external_id:\n 'ee818eebb052cf288ffeeb2e09ee35c9946e1a7f53a959cb3ef06d5d4adb78e8',\n },\n event_metadata: { conversion_id: 'ev-1700000903' },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const signUp: Flow.StepExample = {\n title: 'Sign up',\n description:\n 'A user signup is sent to Reddit as a SignUp conversion with hashed user identifiers.',\n in: getEvent('user signup', {\n id: 'ev-1700000904',\n timestamp: 1700000904,\n data: { method: 'email' },\n user: { id: 'new-user-1', email: 'new@example.com' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'SignUp',\n data: {\n map: {\n user: {\n map: {\n email: 'user.email',\n external_id: 'user.id',\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.904Z',\n event_at_ms: 1700000904,\n event_type: { tracking_type: 'SignUp' },\n user: {\n // sha256('new@example.com')\n email:\n 'f0030501023327437b06e5c6f87df7871b8e704ae608d1d0b7b24fdd2a06c716',\n // sha256('new-user-1')\n external_id:\n 'b45cf5f6ebc2c6974ea3bd9fab19f8cc3a7cf63054727a9fcd22f1fda97d6dde',\n },\n event_metadata: { conversion_id: 'ev-1700000904' },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const search: Flow.StepExample = {\n title: 'Search',\n description:\n 'A site search is sent to Reddit as a Search conversion with an item count in event_metadata.',\n in: getEvent('site search', {\n id: 'ev-1700000905',\n timestamp: 1700000905,\n data: { query: 'walkerOS destinations' },\n user: { id: 'user-101' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'Search',\n data: {\n map: {\n event_metadata: {\n map: {\n item_count: { value: 1 },\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.905Z',\n event_at_ms: 1700000905,\n event_type: { tracking_type: 'Search' },\n user: {},\n event_metadata: {\n conversion_id: 'ev-1700000905',\n item_count: 1,\n },\n },\n ],\n },\n }),\n OPTIONS,\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 { schemas, z } from '@walkeros/core/dev';\nimport { ActionSourceSchema } from './primitives';\n\nconst InputSchema = z.union([\n schemas.MappingSchemas.ValueSchema,\n z.literal(false),\n]);\n\nexport const SettingsSchema = z.object({\n accessToken: z\n .string()\n .min(1)\n .describe(\n 'Reddit Conversion Access Token for Bearer authentication (like rdt_ABC123...)',\n ),\n pixelId: z\n .string()\n .min(1)\n .describe(\n 'Reddit Pixel ID used as the API path parameter (like a2_abcdef123456)',\n ),\n action_source: ActionSourceSchema.describe(\n 'Source of the event (WEBSITE, APP, PHYSICAL_STORE) (like WEBSITE)',\n ).optional(),\n doNotHash: z\n .array(z.string())\n .describe(\"Array of user fields that should not be hashed (like ['email'])\")\n .optional(),\n test_mode: z\n .boolean()\n .describe(\n 'Enable test mode by sending test_mode: true in the request body (like true)',\n )\n .optional(),\n url: z\n .string()\n .url()\n .describe(\n 'Custom URL for Reddit Conversions API endpoint (like https://ads-api.reddit.com/api/v2.0/conversions/events/)',\n )\n .optional(),\n user_data: z\n .record(z.string(), z.string())\n .describe(\n \"Mapping configuration for user fields (like { email: 'user.email', external_id: 'user.id' })\",\n )\n .optional(),\n ip: InputSchema.describe(\n 'Client IP, sent as user.ip_address (hashed). Resolves against { ingest, event }. Default: [\"ingest.ip\", \"event.user.ip\"]. false switches it off. A mapped user.ip_address wins.',\n ).optional(),\n userAgent: InputSchema.describe(\n 'Client user agent, sent as user.user_agent (hashed). Resolves against { ingest, event }. Default: [\"ingest.userAgent\", \"event.user.userAgent\"]. false switches it off. A mapped user.user_agent wins.',\n ).optional(),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\n/**\n * Action Source Enum\n * Where the conversion event took place\n * https://ads-api.reddit.com/docs/v2/#tag/Conversions-API\n */\nexport const ActionSourceSchema = z.enum(['WEBSITE', 'APP', 'PHYSICAL_STORE']);\n\n/**\n * Tracking Type\n * Standard Reddit Conversions API event types.\n * Custom events use `Custom` with a `custom_event_name`.\n */\nexport const TrackingTypeSchema = z.enum([\n 'PageVisit',\n 'ViewContent',\n 'Search',\n 'AddToCart',\n 'AddToWishlist',\n 'Purchase',\n 'Lead',\n 'SignUp',\n 'Custom',\n]);\n","import { z } from '@walkeros/core/dev';\n\n/**\n * Reddit Conversions API Mapping Schema\n * Reddit CAPI has no event-level mapping configuration\n */\nexport const MappingSchema = z.object({});\n\n/**\n * Type inference from MappingSchema\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 Reddit Conversions API destination\n *\n * These environments provide standardized mock structures for testing\n * and development without requiring actual HTTP requests.\n */\n\ntype MockSendServer = (\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: MockSendServer = async () => {\n // Simulate successful Reddit API response\n return {\n ok: true,\n data: {\n success: true,\n },\n };\n};\n\n/**\n * Standard mock environment for push operations\n *\n * Use this for testing Reddit Conversions API events without making\n * actual HTTP requests to Reddit's servers.\n */\nexport const push: Env = {\n sendServer: mockSendServer,\n};\n\nexport const simulation = ['sendServer'];\n","import type { Flow, WalkerOS } from '@walkeros/core';\nimport { getEvent, isObject } from '@walkeros/core';\n\n/**\n * Reddit Conversions API step examples.\n *\n * At push time, the destination calls\n * `env.sendServer(endpoint, JSON.stringify(body), options)` where\n * `endpoint = ${settings.url}${settings.pixelId}` and\n * `body = { data: { events: [hashedServerEvent] } }`.\n *\n * Test fixture pins `pixelId = 'a2_abcdef123456'` and the default url, so\n * every endpoint resolves to:\n * https://ads-api.reddit.com/api/v2.0/conversions/events/a2_abcdef123456\n *\n * The serverEvent keys are assembled in this order (insertion order matters\n * for `JSON.stringify` string equality):\n * 1. event_at (ISO timestamp)\n * 2. event_at_ms\n * 3. event_type ({ tracking_type, custom_event_name? })\n * 4. ...restEventData (mapped event data minus user/event_metadata/click_id)\n * 5. user (hashed - email, external_id, ip_address, user_agent, idfa, aaid)\n * 6. event_metadata (conversion_id first, then merged metadata)\n * 7. click_id (only when a string is present)\n *\n * `options` carries the Authorization: Bearer <accessToken> header.\n */\nconst ENDPOINT =\n 'https://ads-api.reddit.com/api/v2.0/conversions/events/a2_abcdef123456';\nconst OPTIONS = {\n headers: { Authorization: 'Bearer s3cr3t' },\n};\n\nexport const purchase: Flow.StepExample = {\n title: 'Purchase',\n description:\n 'A completed order is sent to the Reddit Conversions API as a Purchase event with value, currency, and items.',\n in: getEvent('order complete', {\n id: 'ev-1700000900',\n timestamp: 1700000900,\n data: { id: 'ORD-300', total: 249.99, currency: 'EUR' },\n nested: [\n {\n entity: 'product',\n data: {\n id: 'SKU-A1',\n name: 'Everyday Ruck Snack',\n category: 'bags',\n price: '129.99',\n quantity: 2,\n },\n },\n ],\n user: { id: 'user-123', device: 'device-456' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'Purchase',\n data: {\n map: {\n event_metadata: {\n map: {\n value_decimal: 'data.total',\n currency: { key: 'data.currency', value: 'EUR' },\n item_count: {\n fn: (event: unknown) =>\n (event as WalkerOS.Event).nested.filter(\n (item) => item.entity === 'product',\n ).length,\n },\n products: {\n loop: [\n 'nested',\n {\n condition: (entity: unknown) =>\n isObject(entity) && entity.entity === 'product',\n map: {\n id: 'data.id',\n name: 'data.name',\n category: { key: 'data.category', value: 'uncategorized' },\n },\n },\n ],\n },\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.900Z',\n event_at_ms: 1700000900,\n event_type: { tracking_type: 'Purchase' },\n user: {},\n event_metadata: {\n conversion_id: 'ev-1700000900',\n value_decimal: 249.99,\n currency: 'EUR',\n item_count: 1,\n products: [\n {\n id: 'SKU-A1',\n name: 'Everyday Ruck Snack',\n category: 'bags',\n },\n ],\n },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const addToCart: Flow.StepExample = {\n title: 'Add to cart',\n description:\n 'A product add is sent to Reddit as an AddToCart conversion with value and product details.',\n in: getEvent('product add', {\n id: 'ev-1700000901',\n timestamp: 1700000901,\n data: {\n id: 'SKU-B2',\n name: 'Cool Cap',\n category: 'hats',\n price: '42.00',\n quantity: 1,\n },\n user: { id: 'user-456' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'AddToCart',\n data: {\n map: {\n event_metadata: {\n map: {\n value_decimal: 'data.price',\n currency: { value: 'EUR' },\n item_count: { value: 1 },\n products: {\n set: [\n {\n map: {\n id: 'data.id',\n name: 'data.name',\n category: { key: 'data.category', value: 'uncategorized' },\n },\n },\n ],\n },\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.901Z',\n event_at_ms: 1700000901,\n event_type: { tracking_type: 'AddToCart' },\n user: {},\n event_metadata: {\n conversion_id: 'ev-1700000901',\n value_decimal: '42.00',\n currency: 'EUR',\n item_count: 1,\n products: [\n { id: 'SKU-B2', name: 'Cool Cap', category: 'hats' },\n ],\n },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const pageVisit: Flow.StepExample = {\n title: 'Page visit',\n description:\n 'A page view is sent to Reddit as a PageVisit conversion used for retargeting audiences.',\n in: getEvent('page view', {\n id: 'ev-1700000902',\n timestamp: 1700000902,\n user: { id: 'user-789' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'PageVisit',\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.902Z',\n event_at_ms: 1700000902,\n event_type: { tracking_type: 'PageVisit' },\n user: {},\n event_metadata: { conversion_id: 'ev-1700000902' },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const lead: Flow.StepExample = {\n title: 'Lead',\n description:\n 'A form submission is sent to Reddit as a Lead conversion with the SHA-256 hashed email and external id.',\n in: getEvent('form submit', {\n id: 'ev-1700000903',\n timestamp: 1700000903,\n data: { form: 'contact' },\n user: { id: 'user-lead-1', email: 'lead@example.com' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'Lead',\n data: {\n map: {\n user: {\n map: {\n email: 'user.email',\n external_id: 'user.id',\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.903Z',\n event_at_ms: 1700000903,\n event_type: { tracking_type: 'Lead' },\n user: {\n // sha256('lead@example.com')\n email:\n '9fbdefe2837a03c9225be80e741f316f4d174d1732b719b6abb6477efc1ae9d2',\n // sha256('user-lead-1')\n external_id:\n 'ee818eebb052cf288ffeeb2e09ee35c9946e1a7f53a959cb3ef06d5d4adb78e8',\n },\n event_metadata: { conversion_id: 'ev-1700000903' },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const signUp: Flow.StepExample = {\n title: 'Sign up',\n description:\n 'A user signup is sent to Reddit as a SignUp conversion with hashed user identifiers.',\n in: getEvent('user signup', {\n id: 'ev-1700000904',\n timestamp: 1700000904,\n data: { method: 'email' },\n user: { id: 'new-user-1', email: 'new@example.com' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'SignUp',\n data: {\n map: {\n user: {\n map: {\n email: 'user.email',\n external_id: 'user.id',\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.904Z',\n event_at_ms: 1700000904,\n event_type: { tracking_type: 'SignUp' },\n user: {\n // sha256('new@example.com')\n email:\n 'f0030501023327437b06e5c6f87df7871b8e704ae608d1d0b7b24fdd2a06c716',\n // sha256('new-user-1')\n external_id:\n 'b45cf5f6ebc2c6974ea3bd9fab19f8cc3a7cf63054727a9fcd22f1fda97d6dde',\n },\n event_metadata: { conversion_id: 'ev-1700000904' },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n\nexport const search: Flow.StepExample = {\n title: 'Search',\n description:\n 'A site search is sent to Reddit as a Search conversion with an item count in event_metadata.',\n in: getEvent('site search', {\n id: 'ev-1700000905',\n timestamp: 1700000905,\n data: { query: 'walkerOS destinations' },\n user: { id: 'user-101' },\n source: { type: 'express', platform: 'server' },\n }),\n mapping: {\n name: 'Search',\n data: {\n map: {\n event_metadata: {\n map: {\n item_count: { value: 1 },\n },\n },\n },\n },\n },\n out: [\n [\n 'sendServer',\n ENDPOINT,\n JSON.stringify({\n data: {\n events: [\n {\n event_at: '1970-01-20T16:13:20.905Z',\n event_at_ms: 1700000905,\n event_type: { tracking_type: 'Search' },\n user: {},\n event_metadata: {\n conversion_id: 'ev-1700000905',\n item_count: 1,\n },\n },\n ],\n },\n }),\n OPTIONS,\n ],\n ],\n};\n"],"mappings":";;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,mBAAmB;;;ACA5B,SAAS,SAAS,KAAAA,UAAS;;;ACA3B,SAAS,SAAS;AAOX,IAAM,qBAAqB,EAAE,KAAK,CAAC,WAAW,OAAO,gBAAgB,CAAC;AAOtE,IAAM,qBAAqB,EAAE,KAAK;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;;;ADrBD,IAAM,cAAcC,GAAE,MAAM;AAAA,EAC1B,QAAQ,eAAe;AAAA,EACvBA,GAAE,QAAQ,KAAK;AACjB,CAAC;AAEM,IAAM,iBAAiBA,GAAE,OAAO;AAAA,EACrC,aAAaA,GACV,OAAO,EACP,IAAI,CAAC,EACL;AAAA,IACC;AAAA,EACF;AAAA,EACF,SAASA,GACN,OAAO,EACP,IAAI,CAAC,EACL;AAAA,IACC;AAAA,EACF;AAAA,EACF,eAAe,mBAAmB;AAAA,IAChC;AAAA,EACF,EAAE,SAAS;AAAA,EACX,WAAWA,GACR,MAAMA,GAAE,OAAO,CAAC,EAChB,SAAS,iEAAiE,EAC1E,SAAS;AAAA,EACZ,WAAWA,GACR,QAAQ,EACR;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,KAAKA,GACF,OAAO,EACP,IAAI,EACJ;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,WAAWA,GACR,OAAOA,GAAE,OAAO,GAAGA,GAAE,OAAO,CAAC,EAC7B;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,IAAI,YAAY;AAAA,IACd;AAAA,EACF,EAAE,SAAS;AAAA,EACX,WAAW,YAAY;AAAA,IACrB;AAAA,EACF,EAAE,SAAS;AACb,CAAC;;;AErDD,SAAS,KAAAC,UAAS;AAMX,IAAM,gBAAgBA,GAAE,OAAO,CAAC,CAAC;;;AHIjC,IAAM,WAAW,YAAY,cAAc;AAC3C,IAAM,UAAU,YAAY,aAAa;;;AIXhD;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAoBA,IAAM,iBAAiC,YAAY;AAEjD,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,MACJ,SAAS;AAAA,IACX;AAAA,EACF;AACF;AAQO,IAAM,OAAY;AAAA,EACvB,YAAY;AACd;AAEO,IAAM,aAAa,CAAC,YAAY;;;ACxCvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,SAAS,UAAU,gBAAgB;AA0BnC,IAAM,WACJ;AACF,IAAM,UAAU;AAAA,EACd,SAAS,EAAE,eAAe,gBAAgB;AAC5C;AAEO,IAAM,WAA6B;AAAA,EACxC,OAAO;AAAA,EACP,aACE;AAAA,EACF,IAAI,SAAS,kBAAkB;AAAA,IAC7B,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,MAAM,EAAE,IAAI,WAAW,OAAO,QAAQ,UAAU,MAAM;AAAA,IACtD,QAAQ;AAAA,MACN;AAAA,QACE,QAAQ;AAAA,QACR,MAAM;AAAA,UACJ,IAAI;AAAA,UACJ,MAAM;AAAA,UACN,UAAU;AAAA,UACV,OAAO;AAAA,UACP,UAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM,EAAE,IAAI,YAAY,QAAQ,aAAa;AAAA,IAC7C,QAAQ,EAAE,MAAM,WAAW,UAAU,SAAS;AAAA,EAChD,CAAC;AAAA,EACD,SAAS;AAAA,IACP,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,KAAK;AAAA,QACH,gBAAgB;AAAA,UACd,KAAK;AAAA,YACH,eAAe;AAAA,YACf,UAAU,EAAE,KAAK,iBAAiB,OAAO,MAAM;AAAA,YAC/C,YAAY;AAAA,cACV,IAAI,CAAC,UACF,MAAyB,OAAO;AAAA,gBAC/B,CAAC,SAAS,KAAK,WAAW;AAAA,cAC5B,EAAE;AAAA,YACN;AAAA,YACA,UAAU;AAAA,cACR,MAAM;AAAA,gBACJ;AAAA,gBACA;AAAA,kBACE,WAAW,CAAC,WACV,SAAS,MAAM,KAAK,OAAO,WAAW;AAAA,kBACxC,KAAK;AAAA,oBACH,IAAI;AAAA,oBACJ,MAAM;AAAA,oBACN,UAAU,EAAE,KAAK,iBAAiB,OAAO,gBAAgB;AAAA,kBAC3D;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,MACA,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,UACJ,QAAQ;AAAA,YACN;AAAA,cACE,UAAU;AAAA,cACV,aAAa;AAAA,cACb,YAAY,EAAE,eAAe,WAAW;AAAA,cACxC,MAAM,CAAC;AAAA,cACP,gBAAgB;AAAA,gBACd,eAAe;AAAA,gBACf,eAAe;AAAA,gBACf,UAAU;AAAA,gBACV,YAAY;AAAA,gBACZ,UAAU;AAAA,kBACR;AAAA,oBACE,IAAI;AAAA,oBACJ,MAAM;AAAA,oBACN,UAAU;AAAA,kBACZ;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,YAA8B;AAAA,EACzC,OAAO;AAAA,EACP,aACE;AAAA,EACF,IAAI,SAAS,eAAe;AAAA,IAC1B,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,MAAM;AAAA,MACJ,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,UAAU;AAAA,MACV,OAAO;AAAA,MACP,UAAU;AAAA,IACZ;AAAA,IACA,MAAM,EAAE,IAAI,WAAW;AAAA,IACvB,QAAQ,EAAE,MAAM,WAAW,UAAU,SAAS;AAAA,EAChD,CAAC;AAAA,EACD,SAAS;AAAA,IACP,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,KAAK;AAAA,QACH,gBAAgB;AAAA,UACd,KAAK;AAAA,YACH,eAAe;AAAA,YACf,UAAU,EAAE,OAAO,MAAM;AAAA,YACzB,YAAY,EAAE,OAAO,EAAE;AAAA,YACvB,UAAU;AAAA,cACR,KAAK;AAAA,gBACH;AAAA,kBACE,KAAK;AAAA,oBACH,IAAI;AAAA,oBACJ,MAAM;AAAA,oBACN,UAAU,EAAE,KAAK,iBAAiB,OAAO,gBAAgB;AAAA,kBAC3D;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,MACA,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,UACJ,QAAQ;AAAA,YACN;AAAA,cACE,UAAU;AAAA,cACV,aAAa;AAAA,cACb,YAAY,EAAE,eAAe,YAAY;AAAA,cACzC,MAAM,CAAC;AAAA,cACP,gBAAgB;AAAA,gBACd,eAAe;AAAA,gBACf,eAAe;AAAA,gBACf,UAAU;AAAA,gBACV,YAAY;AAAA,gBACZ,UAAU;AAAA,kBACR,EAAE,IAAI,UAAU,MAAM,YAAY,UAAU,OAAO;AAAA,gBACrD;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,YAA8B;AAAA,EACzC,OAAO;AAAA,EACP,aACE;AAAA,EACF,IAAI,SAAS,aAAa;AAAA,IACxB,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,MAAM,EAAE,IAAI,WAAW;AAAA,IACvB,QAAQ,EAAE,MAAM,WAAW,UAAU,SAAS;AAAA,EAChD,CAAC;AAAA,EACD,SAAS;AAAA,IACP,MAAM;AAAA,EACR;AAAA,EACA,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,MACA,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,UACJ,QAAQ;AAAA,YACN;AAAA,cACE,UAAU;AAAA,cACV,aAAa;AAAA,cACb,YAAY,EAAE,eAAe,YAAY;AAAA,cACzC,MAAM,CAAC;AAAA,cACP,gBAAgB,EAAE,eAAe,gBAAgB;AAAA,YACnD;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,OAAyB;AAAA,EACpC,OAAO;AAAA,EACP,aACE;AAAA,EACF,IAAI,SAAS,eAAe;AAAA,IAC1B,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,MAAM,EAAE,MAAM,UAAU;AAAA,IACxB,MAAM,EAAE,IAAI,eAAe,OAAO,mBAAmB;AAAA,IACrD,QAAQ,EAAE,MAAM,WAAW,UAAU,SAAS;AAAA,EAChD,CAAC;AAAA,EACD,SAAS;AAAA,IACP,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,KAAK;AAAA,QACH,MAAM;AAAA,UACJ,KAAK;AAAA,YACH,OAAO;AAAA,YACP,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,MACA,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,UACJ,QAAQ;AAAA,YACN;AAAA,cACE,UAAU;AAAA,cACV,aAAa;AAAA,cACb,YAAY,EAAE,eAAe,OAAO;AAAA,cACpC,MAAM;AAAA;AAAA,gBAEJ,OACE;AAAA;AAAA,gBAEF,aACE;AAAA,cACJ;AAAA,cACA,gBAAgB,EAAE,eAAe,gBAAgB;AAAA,YACnD;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,SAA2B;AAAA,EACtC,OAAO;AAAA,EACP,aACE;AAAA,EACF,IAAI,SAAS,eAAe;AAAA,IAC1B,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,MAAM,EAAE,QAAQ,QAAQ;AAAA,IACxB,MAAM,EAAE,IAAI,cAAc,OAAO,kBAAkB;AAAA,IACnD,QAAQ,EAAE,MAAM,WAAW,UAAU,SAAS;AAAA,EAChD,CAAC;AAAA,EACD,SAAS;AAAA,IACP,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,KAAK;AAAA,QACH,MAAM;AAAA,UACJ,KAAK;AAAA,YACH,OAAO;AAAA,YACP,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,MACA,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,UACJ,QAAQ;AAAA,YACN;AAAA,cACE,UAAU;AAAA,cACV,aAAa;AAAA,cACb,YAAY,EAAE,eAAe,SAAS;AAAA,cACtC,MAAM;AAAA;AAAA,gBAEJ,OACE;AAAA;AAAA,gBAEF,aACE;AAAA,cACJ;AAAA,cACA,gBAAgB,EAAE,eAAe,gBAAgB;AAAA,YACnD;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,SAA2B;AAAA,EACtC,OAAO;AAAA,EACP,aACE;AAAA,EACF,IAAI,SAAS,eAAe;AAAA,IAC1B,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,MAAM,EAAE,OAAO,wBAAwB;AAAA,IACvC,MAAM,EAAE,IAAI,WAAW;AAAA,IACvB,QAAQ,EAAE,MAAM,WAAW,UAAU,SAAS;AAAA,EAChD,CAAC;AAAA,EACD,SAAS;AAAA,IACP,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,KAAK;AAAA,QACH,gBAAgB;AAAA,UACd,KAAK;AAAA,YACH,YAAY,EAAE,OAAO,EAAE;AAAA,UACzB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,MACA,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,UACJ,QAAQ;AAAA,YACN;AAAA,cACE,UAAU;AAAA,cACV,aAAa;AAAA,cACb,YAAY,EAAE,eAAe,SAAS;AAAA,cACtC,MAAM,CAAC;AAAA,cACP,gBAAgB;AAAA,gBACd,eAAe;AAAA,gBACf,YAAY;AAAA,cACd;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AACF;","names":["z","z","z"]}
package/dist/index.d.mts CHANGED
@@ -9,6 +9,18 @@ interface Settings {
9
9
  test_mode?: boolean;
10
10
  url?: string;
11
11
  user_data?: Mapping$1.Map;
12
+ /**
13
+ * Client IP, sent as user.ip_address (hashed). Resolves against { ingest, event }.
14
+ * `false` switches it off.
15
+ * @default ['ingest.ip', 'event.user.ip']
16
+ */
17
+ ip?: Mapping$1.Value | false;
18
+ /**
19
+ * Client user agent, sent as user.user_agent (hashed). Resolves against
20
+ * { ingest, event }. `false` switches it off.
21
+ * @default ['ingest.userAgent', 'event.user.userAgent']
22
+ */
23
+ userAgent?: Mapping$1.Value | false;
12
24
  }
13
25
  type InitSettings = Partial<Settings>;
14
26
  interface Mapping {
package/dist/index.d.ts CHANGED
@@ -9,6 +9,18 @@ interface Settings {
9
9
  test_mode?: boolean;
10
10
  url?: string;
11
11
  user_data?: Mapping$1.Map;
12
+ /**
13
+ * Client IP, sent as user.ip_address (hashed). Resolves against { ingest, event }.
14
+ * `false` switches it off.
15
+ * @default ['ingest.ip', 'event.user.ip']
16
+ */
17
+ ip?: Mapping$1.Value | false;
18
+ /**
19
+ * Client user agent, sent as user.user_agent (hashed). Resolves against
20
+ * { ingest, event }. `false` switches it off.
21
+ * @default ['ingest.userAgent', 'event.user.userAgent']
22
+ */
23
+ userAgent?: Mapping$1.Value | false;
12
24
  }
13
25
  type InitSettings = Partial<Settings>;
14
26
  interface Mapping {
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var mod,__defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,index_exports={};((target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})})(index_exports,{DestinationReddit:()=>types_exports,default:()=>index_default,destinationReddit:()=>destinationReddit}),module.exports=(mod=index_exports,((to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to})(__defProp({},"__esModule",{value:!0}),mod));var import_core2=require("@walkeros/core"),import_server_core2=require("@walkeros/server-core"),import_core=require("@walkeros/core"),import_server_core=require("@walkeros/server-core"),keysToHash=["email","external_id","ip_address","user_agent","idfa","aaid"];function shouldBeHashed(key,doNotHash=[]){return keysToHash.includes(key)&&!doNotHash.includes(key)}async function processValue(value,shouldHash){return shouldHash?(0,import_core.isArray)(value)?Promise.all(value.map(item=>(0,import_server_core.getHashServer)(String(item)))):(0,import_server_core.getHashServer)(String(value)):value}var STANDARD_TRACKING_TYPES=new Set(["PageVisit","ViewContent","Search","AddToCart","AddToWishlist","Purchase","Lead","SignUp","Custom"]);function buildEventType(name){return function(name){return STANDARD_TRACKING_TYPES.has(name)}(name)&&"Custom"!==name?{tracking_type:name}:{tracking_type:"Custom",custom_event_name:name}}var push=async function(event,{config:config,data:data,env:env,logger:logger,collector:collector}){const{accessToken:accessToken,pixelId:pixelId,action_source:_action_source,doNotHash:doNotHash,test_mode:test_mode,url:url="https://ads-api.reddit.com/api/v2.0/conversions/events/",user_data:user_data}=config.settings,eventData=(0,import_core2.isObject)(data)?data:{},configData=config.data?await(0,import_core2.getMappingValue)(event,config.data,{collector:collector}):{},userDataCustom=user_data?await(0,import_core2.getMappingValue)(event,{map:user_data},{collector:collector}):{},userData={...(0,import_core2.isObject)(configData)&&(0,import_core2.isObject)(configData.user)?configData.user:{},...(0,import_core2.isObject)(userDataCustom)?userDataCustom:{},...(0,import_core2.isObject)(eventData.user)?eventData.user:{}},{user:_u,event_metadata:eventMetadata,click_id:eventClickId,...restEventData}=eventData,timestampMs=event.timestamp||Date.now(),serverEvent={event_at:new Date(timestampMs).toISOString(),event_at_ms:timestampMs,event_type:buildEventType(event.name),...restEventData,user:userData},metadataFromMapping=(0,import_core2.isObject)(eventMetadata)?eventMetadata:{};serverEvent.event_metadata={conversion_id:event.id,...metadataFromMapping},"string"==typeof eventClickId&&(serverEvent.click_id=eventClickId);const hashedServerEvent=await async function(value,doNotHash=[]){if(!(0,import_core.isObject)(value))return value;const isUser="user"in value,target=isUser?value.user:value,result=(await Promise.all(Object.entries(target).map(async([k,v])=>[k,await processValue(v,isUser&&shouldBeHashed(k,doNotHash))]))).reduce((acc,[k,v])=>((0,import_core.isString)(k)&&(acc[k]=v),acc),{});return isUser?{...value,user:result}:result}(serverEvent,doNotHash),body={...test_mode?{test_mode:!0}:{},data:{events:[hashedServerEvent]}},endpoint=`${url}${pixelId}`;logger.debug("Calling Reddit API",{endpoint:endpoint,method:"POST",trackingType:serverEvent.event_type.tracking_type,eventId:serverEvent.event_metadata?.conversion_id});const sendServerFn=env?.sendServer||import_server_core2.sendServer,result=await sendServerFn(endpoint,JSON.stringify(body),{headers:{Authorization:`Bearer ${accessToken}`}});logger.debug("Reddit API response",{ok:!(0,import_core2.isObject)(result)||result.ok}),(0,import_core2.isObject)(result)&&!1===result.ok&&logger.throw(`Reddit API error: ${JSON.stringify(result)}`)},types_exports={},destinationReddit={type:"reddit",config:{},async init({config:partialConfig,logger:logger}){const config=function(partialConfig={},logger){const settings=partialConfig.settings||{},{accessToken:accessToken,pixelId:pixelId}=settings;accessToken||logger.throw("Config settings accessToken missing"),pixelId||logger.throw("Config settings pixelId missing");const settingsConfig={...settings,accessToken:accessToken,pixelId:pixelId};return{...partialConfig,settings:settingsConfig}}(partialConfig,logger);return config},push:async(event,context)=>await push(event,context)},index_default=destinationReddit;//# sourceMappingURL=index.js.map
1
+ "use strict";var mod,__defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,index_exports={};((target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})})(index_exports,{DestinationReddit:()=>types_exports,default:()=>index_default,destinationReddit:()=>destinationReddit}),module.exports=(mod=index_exports,((to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to})(__defProp({},"__esModule",{value:!0}),mod));var import_core2=require("@walkeros/core"),import_server_core2=require("@walkeros/server-core"),import_core=require("@walkeros/core"),import_server_core=require("@walkeros/server-core"),keysToHash=["email","external_id","ip_address","user_agent","idfa","aaid"];function shouldBeHashed(key,doNotHash=[]){return keysToHash.includes(key)&&!doNotHash.includes(key)}async function processValue(value,shouldHash){return shouldHash?(0,import_core.isArray)(value)?Promise.all(value.map(item=>(0,import_server_core.getHashServer)(String(item)))):(0,import_server_core.getHashServer)(String(value)):value}var STANDARD_TRACKING_TYPES=new Set(["PageVisit","ViewContent","Search","AddToCart","AddToWishlist","Purchase","Lead","SignUp","Custom"]);function buildEventType(name){return function(name){return STANDARD_TRACKING_TYPES.has(name)}(name)&&"Custom"!==name?{tracking_type:name}:{tracking_type:"Custom",custom_event_name:name}}var push=async function(event,{config:config,data:data,ingest:ingest,env:env,logger:logger,collector:collector}){const{accessToken:accessToken,pixelId:pixelId,action_source:_action_source,doNotHash:doNotHash,test_mode:test_mode,url:url="https://ads-api.reddit.com/api/v2.0/conversions/events/",user_data:user_data,ip:ip,userAgent:userAgent}=config.settings,eventData=(0,import_core2.isObject)(data)?data:{},configData=config.data?await(0,import_core2.getMappingValue)(event,config.data,{collector:collector}):{},userDataCustom=user_data?await(0,import_core2.getMappingValue)(event,{map:user_data},{collector:collector}):{},root=(0,import_core2.createMappingRoot)(ingest,event),clientIp=await resolveClient(ip,DEFAULT_IP,root,collector),clientUserAgent=await resolveClient(userAgent,DEFAULT_USER_AGENT,root,collector),userData={...void 0!==clientIp?{ip_address:clientIp}:{},...void 0!==clientUserAgent?{user_agent:clientUserAgent}:{},...(0,import_core2.isObject)(configData)&&(0,import_core2.isObject)(configData.user)?configData.user:{},...(0,import_core2.isObject)(userDataCustom)?userDataCustom:{},...(0,import_core2.isObject)(eventData.user)?eventData.user:{}},{user:_u,event_metadata:eventMetadata,click_id:eventClickId,...restEventData}=eventData,timestampMs=event.timestamp||Date.now(),serverEvent={event_at:new Date(timestampMs).toISOString(),event_at_ms:timestampMs,event_type:buildEventType(event.name),...restEventData,user:userData},metadataFromMapping=(0,import_core2.isObject)(eventMetadata)?eventMetadata:{};serverEvent.event_metadata={conversion_id:event.id,...metadataFromMapping},"string"==typeof eventClickId&&(serverEvent.click_id=eventClickId);const hashedServerEvent=await async function(value,doNotHash=[]){if(!(0,import_core.isObject)(value))return value;const isUser="user"in value,target=isUser?value.user:value,result=(await Promise.all(Object.entries(target).map(async([k,v])=>[k,await processValue(v,isUser&&shouldBeHashed(k,doNotHash))]))).reduce((acc,[k,v])=>((0,import_core.isString)(k)&&(acc[k]=v),acc),{});return isUser?{...value,user:result}:result}(serverEvent,doNotHash),body={...test_mode?{test_mode:!0}:{},data:{events:[hashedServerEvent]}},endpoint=`${url}${pixelId}`;logger.debug("Calling Reddit API",{endpoint:endpoint,method:"POST",trackingType:serverEvent.event_type.tracking_type,eventId:serverEvent.event_metadata?.conversion_id});const sendServerFn=env?.sendServer||import_server_core2.sendServer,result=await sendServerFn(endpoint,JSON.stringify(body),{headers:{Authorization:`Bearer ${accessToken}`}});logger.debug("Reddit API response",{ok:!(0,import_core2.isObject)(result)||result.ok}),(0,import_core2.isObject)(result)&&!1===result.ok&&logger.throw(`Reddit API error: ${JSON.stringify(result)}`)},DEFAULT_IP=["ingest.ip","event.user.ip"],DEFAULT_USER_AGENT=["ingest.userAgent","event.user.userAgent"];async function resolveClient(setting,fallback,root,collector){if(!1===setting)return;const value=await(0,import_core2.getMappingValue)(root,setting??fallback,{collector:collector});return"string"==typeof value&&""!==value?value:void 0}var types_exports={},destinationReddit={type:"reddit",config:{},async init({config:partialConfig,logger:logger}){const config=function(partialConfig={},logger){const settings=partialConfig.settings||{},{accessToken:accessToken,pixelId:pixelId}=settings;accessToken||logger.throw("Config settings accessToken missing"),pixelId||logger.throw("Config settings pixelId missing");const settingsConfig={...settings,accessToken:accessToken,pixelId:pixelId};return{...partialConfig,settings:settingsConfig}}(partialConfig,logger);return config},push:async(event,context)=>await push(event,context)},index_default=destinationReddit;//# 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/hash.ts","../src/types/index.ts"],"sourcesContent":["import type { Destination } from './types';\nimport { getConfig } from './config';\nimport { push } from './push';\n\n// Types\nexport * as DestinationReddit from './types';\n\nexport const destinationReddit: Destination = {\n type: 'reddit',\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 destinationReddit;\n","import type { Config, Settings, PartialConfig } from './types';\nimport type { Logger } from '@walkeros/core';\n\nexport function getConfig(\n partialConfig: PartialConfig = {},\n logger: Logger.Instance,\n): Config {\n const settings = (partialConfig.settings || {}) as Partial<Settings>;\n const { accessToken, pixelId } = settings;\n\n if (!accessToken) logger.throw('Config settings accessToken missing');\n if (!pixelId) logger.throw('Config settings pixelId missing');\n\n const settingsConfig: Settings = {\n ...settings,\n accessToken,\n pixelId,\n };\n\n return { ...partialConfig, settings: settingsConfig };\n}\n","import type {\n ConversionEvent,\n EventType,\n PushFn,\n RequestBody,\n TrackingType,\n UserData,\n} from './types';\nimport { getMappingValue, isObject } from '@walkeros/core';\nimport { sendServer } from '@walkeros/server-core';\nimport { hashEvent } from './hash';\n\nconst STANDARD_TRACKING_TYPES: ReadonlySet<TrackingType> =\n new Set<TrackingType>([\n 'PageVisit',\n 'ViewContent',\n 'Search',\n 'AddToCart',\n 'AddToWishlist',\n 'Purchase',\n 'Lead',\n 'SignUp',\n 'Custom',\n ]);\n\nfunction isStandardTrackingType(name: string): name is TrackingType {\n return STANDARD_TRACKING_TYPES.has(name as TrackingType);\n}\n\nfunction buildEventType(name: string): EventType {\n if (isStandardTrackingType(name) && name !== 'Custom') {\n return { tracking_type: name };\n }\n return { tracking_type: 'Custom', custom_event_name: name };\n}\n\nexport const push: PushFn = async function (\n event,\n { config, data, env, logger, collector },\n) {\n const {\n accessToken,\n pixelId,\n action_source: _action_source,\n doNotHash,\n test_mode,\n url = 'https://ads-api.reddit.com/api/v2.0/conversions/events/',\n user_data,\n } = config.settings!;\n\n const eventData = isObject(data) ? data : {};\n const configData = config.data\n ? await getMappingValue(event, config.data, { collector })\n : {};\n const userDataCustom = user_data\n ? await getMappingValue(event, { map: user_data }, { collector })\n : {};\n\n // Merge user data from config.data, settings.user_data, and event mapping\n const userData: UserData = {\n ...(isObject(configData) && isObject(configData.user)\n ? configData.user\n : {}),\n ...(isObject(userDataCustom) ? userDataCustom : {}),\n ...(isObject(eventData.user) ? eventData.user : {}),\n };\n\n const {\n user: _u,\n event_metadata: eventMetadata,\n click_id: eventClickId,\n ...restEventData\n } = eventData;\n\n const timestampMs = event.timestamp || Date.now();\n\n const serverEvent: ConversionEvent = {\n event_at: new Date(timestampMs).toISOString(),\n event_at_ms: timestampMs,\n event_type: buildEventType(event.name),\n ...restEventData,\n user: userData,\n };\n\n // Merge event_metadata with auto-populated conversion_id (event.id for dedup)\n const metadataFromMapping = isObject(eventMetadata) ? eventMetadata : {};\n serverEvent.event_metadata = {\n conversion_id: event.id,\n ...metadataFromMapping,\n };\n\n if (typeof eventClickId === 'string') {\n serverEvent.click_id = eventClickId;\n }\n\n const hashedServerEvent = await hashEvent(serverEvent, doNotHash);\n\n const body: RequestBody = {\n ...(test_mode ? { test_mode: true } : {}),\n data: { events: [hashedServerEvent] },\n };\n\n const endpoint = `${url}${pixelId}`;\n\n logger.debug('Calling Reddit API', {\n endpoint,\n method: 'POST',\n trackingType: serverEvent.event_type.tracking_type,\n eventId: serverEvent.event_metadata?.conversion_id,\n });\n\n const sendServerFn = env?.sendServer || sendServer;\n const result = await sendServerFn(endpoint, JSON.stringify(body), {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n logger.debug('Reddit API response', {\n ok: isObject(result) ? result.ok : true,\n });\n\n if (isObject(result) && result.ok === false) {\n logger.throw(`Reddit API error: ${JSON.stringify(result)}`);\n }\n};\n","import { WalkerOS } from '@walkeros/core';\nimport { isArray, isObject, isString } from '@walkeros/core';\nimport { getHashServer } from '@walkeros/server-core';\n\nconst keysToHash = [\n 'email',\n 'external_id',\n 'ip_address',\n 'user_agent',\n 'idfa',\n 'aaid',\n];\n\nfunction shouldBeHashed(key: string, doNotHash: string[] = []): boolean {\n return keysToHash.includes(key) && !doNotHash.includes(key);\n}\n\ntype HashableValue = WalkerOS.AnyObject | unknown | unknown[];\n\nasync function processValue(\n value: unknown,\n shouldHash: boolean,\n): Promise<unknown> {\n if (!shouldHash) return value;\n if (isArray(value)) {\n return Promise.all(value.map((item) => getHashServer(String(item))));\n }\n return getHashServer(String(value));\n}\n\nexport async function hashEvent<T extends HashableValue>(\n value: T,\n doNotHash: string[] = [],\n): Promise<T> {\n if (!isObject(value)) return value;\n\n const isUser = 'user' in value;\n const target = (isUser ? value.user : value) as WalkerOS.AnyObject;\n\n const entries = await Promise.all(\n Object.entries(target).map(async ([k, v]) => [\n k,\n await processValue(v, isUser && shouldBeHashed(k, doNotHash)),\n ]),\n );\n\n const result = entries.reduce((acc, [k, v]) => {\n if (isString(k)) acc[k] = v;\n return acc;\n }, {} as WalkerOS.AnyObject);\n\n return isUser ? { ...value, user: result } : (result as T);\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 accessToken: string;\n pixelId: string;\n action_source?: ActionSource;\n doNotHash?: string[];\n test_mode?: boolean;\n url?: string;\n user_data?: WalkerOSMapping.Map;\n}\n\nexport type InitSettings = Partial<Settings>;\n\nexport interface Mapping {}\n\nexport interface Env extends DestinationServer.Env {\n sendServer?: typeof sendServer;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport interface Destination extends DestinationServer.Destination<Types> {\n init: DestinationServer.InitFn<Types>;\n}\n\nexport type Config = {\n settings: Settings;\n} & DestinationServer.Config<Types>;\n\nexport type InitFn = DestinationServer.InitFn<Types>;\nexport type PushFn = DestinationServer.PushFn<Types>;\n\nexport type PartialConfig = DestinationServer.PartialConfig<Types>;\n\nexport type PushEvents = DestinationServer.PushEvents<Mapping>;\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n\n// Reddit Conversions API types\n// https://ads-api.reddit.com/docs/v2/#tag/Conversions-API\n\nexport type ActionSource = 'WEBSITE' | 'APP' | 'PHYSICAL_STORE';\n\nexport type TrackingType =\n | 'PageVisit'\n | 'ViewContent'\n | 'Search'\n | 'AddToCart'\n | 'AddToWishlist'\n | 'Purchase'\n | 'Lead'\n | 'SignUp'\n | 'Custom';\n\nexport interface EventType {\n tracking_type: TrackingType;\n custom_event_name?: string;\n}\n\nexport interface Product {\n id: string;\n name?: string;\n category: string;\n}\n\nexport interface EventMetadata {\n conversion_id?: string;\n item_count?: number;\n currency?: string;\n value?: number;\n value_decimal?: number;\n products?: Product[];\n units_sold?: number;\n country_code?: string;\n}\n\nexport interface ScreenDimensions {\n width: number;\n height: number;\n}\n\nexport interface DataProcessingOptions {\n modes: string[];\n country?: string;\n region?: string;\n}\n\nexport interface UserData {\n // Hashable fields (SHA-256)\n email?: string;\n external_id?: string;\n ip_address?: string;\n user_agent?: string;\n idfa?: string;\n aaid?: string;\n\n // Pass-through fields (not hashed)\n uuid?: string;\n opt_out?: boolean;\n screen_dimensions?: ScreenDimensions;\n data_processing_options?: DataProcessingOptions;\n}\n\nexport interface ConversionEvent {\n click_id?: string;\n event_at: string;\n event_at_ms?: number;\n event_type: EventType;\n event_metadata?: EventMetadata;\n user: UserData;\n}\n\nexport interface RequestBody {\n test_mode?: boolean;\n data: {\n events: ConversionEvent[];\n };\n}\n\nexport interface ResponseBody {\n success?: boolean;\n message?: string;\n}\n"],"mappings":"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","names":["import_core","import_server_core"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/push.ts","../src/hash.ts","../src/types/index.ts"],"sourcesContent":["import type { Destination } from './types';\nimport { getConfig } from './config';\nimport { push } from './push';\n\n// Types\nexport * as DestinationReddit from './types';\n\nexport const destinationReddit: Destination = {\n type: 'reddit',\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 destinationReddit;\n","import type { Config, Settings, PartialConfig } from './types';\nimport type { Logger } from '@walkeros/core';\n\nexport function getConfig(\n partialConfig: PartialConfig = {},\n logger: Logger.Instance,\n): Config {\n const settings = (partialConfig.settings || {}) as Partial<Settings>;\n const { accessToken, pixelId } = settings;\n\n if (!accessToken) logger.throw('Config settings accessToken missing');\n if (!pixelId) logger.throw('Config settings pixelId missing');\n\n const settingsConfig: Settings = {\n ...settings,\n accessToken,\n pixelId,\n };\n\n return { ...partialConfig, settings: settingsConfig };\n}\n","import type {\n ConversionEvent,\n EventType,\n PushFn,\n RequestBody,\n TrackingType,\n UserData,\n} from './types';\nimport type { Collector, Mapping as WalkerOSMapping } from '@walkeros/core';\nimport { createMappingRoot, getMappingValue, isObject } from '@walkeros/core';\nimport { sendServer } from '@walkeros/server-core';\nimport { hashEvent } from './hash';\n\nconst STANDARD_TRACKING_TYPES: ReadonlySet<TrackingType> =\n new Set<TrackingType>([\n 'PageVisit',\n 'ViewContent',\n 'Search',\n 'AddToCart',\n 'AddToWishlist',\n 'Purchase',\n 'Lead',\n 'SignUp',\n 'Custom',\n ]);\n\nfunction isStandardTrackingType(name: string): name is TrackingType {\n return STANDARD_TRACKING_TYPES.has(name as TrackingType);\n}\n\nfunction buildEventType(name: string): EventType {\n if (isStandardTrackingType(name) && name !== 'Custom') {\n return { tracking_type: name };\n }\n return { tracking_type: 'Custom', custom_event_name: name };\n}\n\nexport const push: PushFn = async function (\n event,\n { config, data, ingest, env, logger, collector },\n) {\n const {\n accessToken,\n pixelId,\n action_source: _action_source,\n doNotHash,\n test_mode,\n url = 'https://ads-api.reddit.com/api/v2.0/conversions/events/',\n user_data,\n ip,\n userAgent,\n } = config.settings!;\n\n const eventData = isObject(data) ? data : {};\n const configData = config.data\n ? await getMappingValue(event, config.data, { collector })\n : {};\n const userDataCustom = user_data\n ? await getMappingValue(event, { map: user_data }, { collector })\n : {};\n\n // Merge user data from config.data, settings.user_data, and event mapping\n // Client IP and user agent auto-fill, overridden by any mapped value\n const root = createMappingRoot(ingest, event);\n const clientIp = await resolveClient(ip, DEFAULT_IP, root, collector);\n const clientUserAgent = await resolveClient(\n userAgent,\n DEFAULT_USER_AGENT,\n root,\n collector,\n );\n\n const userData: UserData = {\n ...(clientIp !== undefined ? { ip_address: clientIp } : {}),\n ...(clientUserAgent !== undefined ? { user_agent: clientUserAgent } : {}),\n ...(isObject(configData) && isObject(configData.user)\n ? configData.user\n : {}),\n ...(isObject(userDataCustom) ? userDataCustom : {}),\n ...(isObject(eventData.user) ? eventData.user : {}),\n };\n\n const {\n user: _u,\n event_metadata: eventMetadata,\n click_id: eventClickId,\n ...restEventData\n } = eventData;\n\n const timestampMs = event.timestamp || Date.now();\n\n const serverEvent: ConversionEvent = {\n event_at: new Date(timestampMs).toISOString(),\n event_at_ms: timestampMs,\n event_type: buildEventType(event.name),\n ...restEventData,\n user: userData,\n };\n\n // Merge event_metadata with auto-populated conversion_id (event.id for dedup)\n const metadataFromMapping = isObject(eventMetadata) ? eventMetadata : {};\n serverEvent.event_metadata = {\n conversion_id: event.id,\n ...metadataFromMapping,\n };\n\n if (typeof eventClickId === 'string') {\n serverEvent.click_id = eventClickId;\n }\n\n const hashedServerEvent = await hashEvent(serverEvent, doNotHash);\n\n const body: RequestBody = {\n ...(test_mode ? { test_mode: true } : {}),\n data: { events: [hashedServerEvent] },\n };\n\n const endpoint = `${url}${pixelId}`;\n\n logger.debug('Calling Reddit API', {\n endpoint,\n method: 'POST',\n trackingType: serverEvent.event_type.tracking_type,\n eventId: serverEvent.event_metadata?.conversion_id,\n });\n\n const sendServerFn = env?.sendServer || sendServer;\n const result = await sendServerFn(endpoint, JSON.stringify(body), {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n logger.debug('Reddit API response', {\n ok: isObject(result) ? result.ok : true,\n });\n\n if (isObject(result) && result.ok === false) {\n logger.throw(`Reddit API error: ${JSON.stringify(result)}`);\n }\n};\n\nconst DEFAULT_IP: WalkerOSMapping.Value = ['ingest.ip', 'event.user.ip'];\nconst DEFAULT_USER_AGENT: WalkerOSMapping.Value = [\n 'ingest.userAgent',\n 'event.user.userAgent',\n];\n\n/** A client IP or user agent setting resolved to a non-empty string. */\nasync function resolveClient(\n setting: WalkerOSMapping.Value | false | undefined,\n fallback: WalkerOSMapping.Value,\n root: WalkerOSMapping.Root,\n collector: Collector.Instance,\n): Promise<string | undefined> {\n if (setting === false) return undefined;\n const value = await getMappingValue(root, setting ?? fallback, { collector });\n return typeof value === 'string' && value !== '' ? value : undefined;\n}\n","import { WalkerOS } from '@walkeros/core';\nimport { isArray, isObject, isString } from '@walkeros/core';\nimport { getHashServer } from '@walkeros/server-core';\n\nconst keysToHash = [\n 'email',\n 'external_id',\n 'ip_address',\n 'user_agent',\n 'idfa',\n 'aaid',\n];\n\nfunction shouldBeHashed(key: string, doNotHash: string[] = []): boolean {\n return keysToHash.includes(key) && !doNotHash.includes(key);\n}\n\ntype HashableValue = WalkerOS.AnyObject | unknown | unknown[];\n\nasync function processValue(\n value: unknown,\n shouldHash: boolean,\n): Promise<unknown> {\n if (!shouldHash) return value;\n if (isArray(value)) {\n return Promise.all(value.map((item) => getHashServer(String(item))));\n }\n return getHashServer(String(value));\n}\n\nexport async function hashEvent<T extends HashableValue>(\n value: T,\n doNotHash: string[] = [],\n): Promise<T> {\n if (!isObject(value)) return value;\n\n const isUser = 'user' in value;\n const target = (isUser ? value.user : value) as WalkerOS.AnyObject;\n\n const entries = await Promise.all(\n Object.entries(target).map(async ([k, v]) => [\n k,\n await processValue(v, isUser && shouldBeHashed(k, doNotHash)),\n ]),\n );\n\n const result = entries.reduce((acc, [k, v]) => {\n if (isString(k)) acc[k] = v;\n return acc;\n }, {} as WalkerOS.AnyObject);\n\n return isUser ? { ...value, user: result } : (result as T);\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 accessToken: string;\n pixelId: string;\n action_source?: ActionSource;\n doNotHash?: string[];\n test_mode?: boolean;\n url?: string;\n user_data?: WalkerOSMapping.Map;\n /**\n * Client IP, sent as user.ip_address (hashed). Resolves against { ingest, event }.\n * `false` switches it off.\n * @default ['ingest.ip', 'event.user.ip']\n */\n ip?: WalkerOSMapping.Value | false;\n /**\n * Client user agent, sent as user.user_agent (hashed). Resolves against\n * { ingest, event }. `false` switches it off.\n * @default ['ingest.userAgent', 'event.user.userAgent']\n */\n userAgent?: WalkerOSMapping.Value | false;\n}\n\nexport type InitSettings = Partial<Settings>;\n\nexport interface Mapping {}\n\nexport interface Env extends DestinationServer.Env {\n sendServer?: typeof sendServer;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport interface Destination extends DestinationServer.Destination<Types> {\n init: DestinationServer.InitFn<Types>;\n}\n\nexport type Config = {\n settings: Settings;\n} & DestinationServer.Config<Types>;\n\nexport type InitFn = DestinationServer.InitFn<Types>;\nexport type PushFn = DestinationServer.PushFn<Types>;\n\nexport type PartialConfig = DestinationServer.PartialConfig<Types>;\n\nexport type PushEvents = DestinationServer.PushEvents<Mapping>;\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n\n// Reddit Conversions API types\n// https://ads-api.reddit.com/docs/v2/#tag/Conversions-API\n\nexport type ActionSource = 'WEBSITE' | 'APP' | 'PHYSICAL_STORE';\n\nexport type TrackingType =\n | 'PageVisit'\n | 'ViewContent'\n | 'Search'\n | 'AddToCart'\n | 'AddToWishlist'\n | 'Purchase'\n | 'Lead'\n | 'SignUp'\n | 'Custom';\n\nexport interface EventType {\n tracking_type: TrackingType;\n custom_event_name?: string;\n}\n\nexport interface Product {\n id: string;\n name?: string;\n category: string;\n}\n\nexport interface EventMetadata {\n conversion_id?: string;\n item_count?: number;\n currency?: string;\n value?: number;\n value_decimal?: number;\n products?: Product[];\n units_sold?: number;\n country_code?: string;\n}\n\nexport interface ScreenDimensions {\n width: number;\n height: number;\n}\n\nexport interface DataProcessingOptions {\n modes: string[];\n country?: string;\n region?: string;\n}\n\nexport interface UserData {\n // Hashable fields (SHA-256)\n email?: string;\n external_id?: string;\n ip_address?: string;\n user_agent?: string;\n idfa?: string;\n aaid?: string;\n\n // Pass-through fields (not hashed)\n uuid?: string;\n opt_out?: boolean;\n screen_dimensions?: ScreenDimensions;\n data_processing_options?: DataProcessingOptions;\n}\n\nexport interface ConversionEvent {\n click_id?: string;\n event_at: string;\n event_at_ms?: number;\n event_type: EventType;\n event_metadata?: EventMetadata;\n user: UserData;\n}\n\nexport interface RequestBody {\n test_mode?: boolean;\n data: {\n events: ConversionEvent[];\n };\n}\n\nexport interface ResponseBody {\n success?: boolean;\n message?: string;\n}\n"],"mappings":"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","names":["import_core","import_server_core"]}
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{getMappingValue,isObject as isObject2}from"@walkeros/core";import{sendServer}from"@walkeros/server-core";import{isArray,isObject,isString}from"@walkeros/core";import{getHashServer}from"@walkeros/server-core";var keysToHash=["email","external_id","ip_address","user_agent","idfa","aaid"];function shouldBeHashed(key,doNotHash=[]){return keysToHash.includes(key)&&!doNotHash.includes(key)}async function processValue(value,shouldHash){return shouldHash?isArray(value)?Promise.all(value.map(item=>getHashServer(String(item)))):getHashServer(String(value)):value}var STANDARD_TRACKING_TYPES=new Set(["PageVisit","ViewContent","Search","AddToCart","AddToWishlist","Purchase","Lead","SignUp","Custom"]);function buildEventType(name){return function(name){return STANDARD_TRACKING_TYPES.has(name)}(name)&&"Custom"!==name?{tracking_type:name}:{tracking_type:"Custom",custom_event_name:name}}var push=async function(event,{config:config,data:data,env:env,logger:logger,collector:collector}){const{accessToken:accessToken,pixelId:pixelId,action_source:_action_source,doNotHash:doNotHash,test_mode:test_mode,url:url="https://ads-api.reddit.com/api/v2.0/conversions/events/",user_data:user_data}=config.settings,eventData=isObject2(data)?data:{},configData=config.data?await getMappingValue(event,config.data,{collector:collector}):{},userDataCustom=user_data?await getMappingValue(event,{map:user_data},{collector:collector}):{},userData={...isObject2(configData)&&isObject2(configData.user)?configData.user:{},...isObject2(userDataCustom)?userDataCustom:{},...isObject2(eventData.user)?eventData.user:{}},{user:_u,event_metadata:eventMetadata,click_id:eventClickId,...restEventData}=eventData,timestampMs=event.timestamp||Date.now(),serverEvent={event_at:new Date(timestampMs).toISOString(),event_at_ms:timestampMs,event_type:buildEventType(event.name),...restEventData,user:userData},metadataFromMapping=isObject2(eventMetadata)?eventMetadata:{};serverEvent.event_metadata={conversion_id:event.id,...metadataFromMapping},"string"==typeof eventClickId&&(serverEvent.click_id=eventClickId);const hashedServerEvent=await async function(value,doNotHash=[]){if(!isObject(value))return value;const isUser="user"in value,target=isUser?value.user:value,result=(await Promise.all(Object.entries(target).map(async([k,v])=>[k,await processValue(v,isUser&&shouldBeHashed(k,doNotHash))]))).reduce((acc,[k,v])=>(isString(k)&&(acc[k]=v),acc),{});return isUser?{...value,user:result}:result}(serverEvent,doNotHash),body={...test_mode?{test_mode:!0}:{},data:{events:[hashedServerEvent]}},endpoint=`${url}${pixelId}`;logger.debug("Calling Reddit API",{endpoint:endpoint,method:"POST",trackingType:serverEvent.event_type.tracking_type,eventId:serverEvent.event_metadata?.conversion_id});const sendServerFn=env?.sendServer||sendServer,result=await sendServerFn(endpoint,JSON.stringify(body),{headers:{Authorization:`Bearer ${accessToken}`}});logger.debug("Reddit API response",{ok:!isObject2(result)||result.ok}),isObject2(result)&&!1===result.ok&&logger.throw(`Reddit API error: ${JSON.stringify(result)}`)},types_exports={},destinationReddit={type:"reddit",config:{},async init({config:partialConfig,logger:logger}){const config=function(partialConfig={},logger){const settings=partialConfig.settings||{},{accessToken:accessToken,pixelId:pixelId}=settings;accessToken||logger.throw("Config settings accessToken missing"),pixelId||logger.throw("Config settings pixelId missing");const settingsConfig={...settings,accessToken:accessToken,pixelId:pixelId};return{...partialConfig,settings:settingsConfig}}(partialConfig,logger);return config},push:async(event,context)=>await push(event,context)},index_default=destinationReddit;export{types_exports as DestinationReddit,index_default as default,destinationReddit};//# sourceMappingURL=index.mjs.map
1
+ import{createMappingRoot,getMappingValue,isObject as isObject2}from"@walkeros/core";import{sendServer}from"@walkeros/server-core";import{isArray,isObject,isString}from"@walkeros/core";import{getHashServer}from"@walkeros/server-core";var keysToHash=["email","external_id","ip_address","user_agent","idfa","aaid"];function shouldBeHashed(key,doNotHash=[]){return keysToHash.includes(key)&&!doNotHash.includes(key)}async function processValue(value,shouldHash){return shouldHash?isArray(value)?Promise.all(value.map(item=>getHashServer(String(item)))):getHashServer(String(value)):value}var STANDARD_TRACKING_TYPES=new Set(["PageVisit","ViewContent","Search","AddToCart","AddToWishlist","Purchase","Lead","SignUp","Custom"]);function buildEventType(name){return function(name){return STANDARD_TRACKING_TYPES.has(name)}(name)&&"Custom"!==name?{tracking_type:name}:{tracking_type:"Custom",custom_event_name:name}}var push=async function(event,{config:config,data:data,ingest:ingest,env:env,logger:logger,collector:collector}){const{accessToken:accessToken,pixelId:pixelId,action_source:_action_source,doNotHash:doNotHash,test_mode:test_mode,url:url="https://ads-api.reddit.com/api/v2.0/conversions/events/",user_data:user_data,ip:ip,userAgent:userAgent}=config.settings,eventData=isObject2(data)?data:{},configData=config.data?await getMappingValue(event,config.data,{collector:collector}):{},userDataCustom=user_data?await getMappingValue(event,{map:user_data},{collector:collector}):{},root=createMappingRoot(ingest,event),clientIp=await resolveClient(ip,DEFAULT_IP,root,collector),clientUserAgent=await resolveClient(userAgent,DEFAULT_USER_AGENT,root,collector),userData={...void 0!==clientIp?{ip_address:clientIp}:{},...void 0!==clientUserAgent?{user_agent:clientUserAgent}:{},...isObject2(configData)&&isObject2(configData.user)?configData.user:{},...isObject2(userDataCustom)?userDataCustom:{},...isObject2(eventData.user)?eventData.user:{}},{user:_u,event_metadata:eventMetadata,click_id:eventClickId,...restEventData}=eventData,timestampMs=event.timestamp||Date.now(),serverEvent={event_at:new Date(timestampMs).toISOString(),event_at_ms:timestampMs,event_type:buildEventType(event.name),...restEventData,user:userData},metadataFromMapping=isObject2(eventMetadata)?eventMetadata:{};serverEvent.event_metadata={conversion_id:event.id,...metadataFromMapping},"string"==typeof eventClickId&&(serverEvent.click_id=eventClickId);const hashedServerEvent=await async function(value,doNotHash=[]){if(!isObject(value))return value;const isUser="user"in value,target=isUser?value.user:value,result=(await Promise.all(Object.entries(target).map(async([k,v])=>[k,await processValue(v,isUser&&shouldBeHashed(k,doNotHash))]))).reduce((acc,[k,v])=>(isString(k)&&(acc[k]=v),acc),{});return isUser?{...value,user:result}:result}(serverEvent,doNotHash),body={...test_mode?{test_mode:!0}:{},data:{events:[hashedServerEvent]}},endpoint=`${url}${pixelId}`;logger.debug("Calling Reddit API",{endpoint:endpoint,method:"POST",trackingType:serverEvent.event_type.tracking_type,eventId:serverEvent.event_metadata?.conversion_id});const sendServerFn=env?.sendServer||sendServer,result=await sendServerFn(endpoint,JSON.stringify(body),{headers:{Authorization:`Bearer ${accessToken}`}});logger.debug("Reddit API response",{ok:!isObject2(result)||result.ok}),isObject2(result)&&!1===result.ok&&logger.throw(`Reddit API error: ${JSON.stringify(result)}`)},DEFAULT_IP=["ingest.ip","event.user.ip"],DEFAULT_USER_AGENT=["ingest.userAgent","event.user.userAgent"];async function resolveClient(setting,fallback,root,collector){if(!1===setting)return;const value=await getMappingValue(root,setting??fallback,{collector:collector});return"string"==typeof value&&""!==value?value:void 0}var types_exports={},destinationReddit={type:"reddit",config:{},async init({config:partialConfig,logger:logger}){const config=function(partialConfig={},logger){const settings=partialConfig.settings||{},{accessToken:accessToken,pixelId:pixelId}=settings;accessToken||logger.throw("Config settings accessToken missing"),pixelId||logger.throw("Config settings pixelId missing");const settingsConfig={...settings,accessToken:accessToken,pixelId:pixelId};return{...partialConfig,settings:settingsConfig}}(partialConfig,logger);return config},push:async(event,context)=>await push(event,context)},index_default=destinationReddit;export{types_exports as DestinationReddit,index_default as default,destinationReddit};//# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config.ts","../src/push.ts","../src/hash.ts","../src/types/index.ts","../src/index.ts"],"sourcesContent":["import type { Config, Settings, PartialConfig } from './types';\nimport type { Logger } from '@walkeros/core';\n\nexport function getConfig(\n partialConfig: PartialConfig = {},\n logger: Logger.Instance,\n): Config {\n const settings = (partialConfig.settings || {}) as Partial<Settings>;\n const { accessToken, pixelId } = settings;\n\n if (!accessToken) logger.throw('Config settings accessToken missing');\n if (!pixelId) logger.throw('Config settings pixelId missing');\n\n const settingsConfig: Settings = {\n ...settings,\n accessToken,\n pixelId,\n };\n\n return { ...partialConfig, settings: settingsConfig };\n}\n","import type {\n ConversionEvent,\n EventType,\n PushFn,\n RequestBody,\n TrackingType,\n UserData,\n} from './types';\nimport { getMappingValue, isObject } from '@walkeros/core';\nimport { sendServer } from '@walkeros/server-core';\nimport { hashEvent } from './hash';\n\nconst STANDARD_TRACKING_TYPES: ReadonlySet<TrackingType> =\n new Set<TrackingType>([\n 'PageVisit',\n 'ViewContent',\n 'Search',\n 'AddToCart',\n 'AddToWishlist',\n 'Purchase',\n 'Lead',\n 'SignUp',\n 'Custom',\n ]);\n\nfunction isStandardTrackingType(name: string): name is TrackingType {\n return STANDARD_TRACKING_TYPES.has(name as TrackingType);\n}\n\nfunction buildEventType(name: string): EventType {\n if (isStandardTrackingType(name) && name !== 'Custom') {\n return { tracking_type: name };\n }\n return { tracking_type: 'Custom', custom_event_name: name };\n}\n\nexport const push: PushFn = async function (\n event,\n { config, data, env, logger, collector },\n) {\n const {\n accessToken,\n pixelId,\n action_source: _action_source,\n doNotHash,\n test_mode,\n url = 'https://ads-api.reddit.com/api/v2.0/conversions/events/',\n user_data,\n } = config.settings!;\n\n const eventData = isObject(data) ? data : {};\n const configData = config.data\n ? await getMappingValue(event, config.data, { collector })\n : {};\n const userDataCustom = user_data\n ? await getMappingValue(event, { map: user_data }, { collector })\n : {};\n\n // Merge user data from config.data, settings.user_data, and event mapping\n const userData: UserData = {\n ...(isObject(configData) && isObject(configData.user)\n ? configData.user\n : {}),\n ...(isObject(userDataCustom) ? userDataCustom : {}),\n ...(isObject(eventData.user) ? eventData.user : {}),\n };\n\n const {\n user: _u,\n event_metadata: eventMetadata,\n click_id: eventClickId,\n ...restEventData\n } = eventData;\n\n const timestampMs = event.timestamp || Date.now();\n\n const serverEvent: ConversionEvent = {\n event_at: new Date(timestampMs).toISOString(),\n event_at_ms: timestampMs,\n event_type: buildEventType(event.name),\n ...restEventData,\n user: userData,\n };\n\n // Merge event_metadata with auto-populated conversion_id (event.id for dedup)\n const metadataFromMapping = isObject(eventMetadata) ? eventMetadata : {};\n serverEvent.event_metadata = {\n conversion_id: event.id,\n ...metadataFromMapping,\n };\n\n if (typeof eventClickId === 'string') {\n serverEvent.click_id = eventClickId;\n }\n\n const hashedServerEvent = await hashEvent(serverEvent, doNotHash);\n\n const body: RequestBody = {\n ...(test_mode ? { test_mode: true } : {}),\n data: { events: [hashedServerEvent] },\n };\n\n const endpoint = `${url}${pixelId}`;\n\n logger.debug('Calling Reddit API', {\n endpoint,\n method: 'POST',\n trackingType: serverEvent.event_type.tracking_type,\n eventId: serverEvent.event_metadata?.conversion_id,\n });\n\n const sendServerFn = env?.sendServer || sendServer;\n const result = await sendServerFn(endpoint, JSON.stringify(body), {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n logger.debug('Reddit API response', {\n ok: isObject(result) ? result.ok : true,\n });\n\n if (isObject(result) && result.ok === false) {\n logger.throw(`Reddit API error: ${JSON.stringify(result)}`);\n }\n};\n","import { WalkerOS } from '@walkeros/core';\nimport { isArray, isObject, isString } from '@walkeros/core';\nimport { getHashServer } from '@walkeros/server-core';\n\nconst keysToHash = [\n 'email',\n 'external_id',\n 'ip_address',\n 'user_agent',\n 'idfa',\n 'aaid',\n];\n\nfunction shouldBeHashed(key: string, doNotHash: string[] = []): boolean {\n return keysToHash.includes(key) && !doNotHash.includes(key);\n}\n\ntype HashableValue = WalkerOS.AnyObject | unknown | unknown[];\n\nasync function processValue(\n value: unknown,\n shouldHash: boolean,\n): Promise<unknown> {\n if (!shouldHash) return value;\n if (isArray(value)) {\n return Promise.all(value.map((item) => getHashServer(String(item))));\n }\n return getHashServer(String(value));\n}\n\nexport async function hashEvent<T extends HashableValue>(\n value: T,\n doNotHash: string[] = [],\n): Promise<T> {\n if (!isObject(value)) return value;\n\n const isUser = 'user' in value;\n const target = (isUser ? value.user : value) as WalkerOS.AnyObject;\n\n const entries = await Promise.all(\n Object.entries(target).map(async ([k, v]) => [\n k,\n await processValue(v, isUser && shouldBeHashed(k, doNotHash)),\n ]),\n );\n\n const result = entries.reduce((acc, [k, v]) => {\n if (isString(k)) acc[k] = v;\n return acc;\n }, {} as WalkerOS.AnyObject);\n\n return isUser ? { ...value, user: result } : (result as T);\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 accessToken: string;\n pixelId: string;\n action_source?: ActionSource;\n doNotHash?: string[];\n test_mode?: boolean;\n url?: string;\n user_data?: WalkerOSMapping.Map;\n}\n\nexport type InitSettings = Partial<Settings>;\n\nexport interface Mapping {}\n\nexport interface Env extends DestinationServer.Env {\n sendServer?: typeof sendServer;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport interface Destination extends DestinationServer.Destination<Types> {\n init: DestinationServer.InitFn<Types>;\n}\n\nexport type Config = {\n settings: Settings;\n} & DestinationServer.Config<Types>;\n\nexport type InitFn = DestinationServer.InitFn<Types>;\nexport type PushFn = DestinationServer.PushFn<Types>;\n\nexport type PartialConfig = DestinationServer.PartialConfig<Types>;\n\nexport type PushEvents = DestinationServer.PushEvents<Mapping>;\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n\n// Reddit Conversions API types\n// https://ads-api.reddit.com/docs/v2/#tag/Conversions-API\n\nexport type ActionSource = 'WEBSITE' | 'APP' | 'PHYSICAL_STORE';\n\nexport type TrackingType =\n | 'PageVisit'\n | 'ViewContent'\n | 'Search'\n | 'AddToCart'\n | 'AddToWishlist'\n | 'Purchase'\n | 'Lead'\n | 'SignUp'\n | 'Custom';\n\nexport interface EventType {\n tracking_type: TrackingType;\n custom_event_name?: string;\n}\n\nexport interface Product {\n id: string;\n name?: string;\n category: string;\n}\n\nexport interface EventMetadata {\n conversion_id?: string;\n item_count?: number;\n currency?: string;\n value?: number;\n value_decimal?: number;\n products?: Product[];\n units_sold?: number;\n country_code?: string;\n}\n\nexport interface ScreenDimensions {\n width: number;\n height: number;\n}\n\nexport interface DataProcessingOptions {\n modes: string[];\n country?: string;\n region?: string;\n}\n\nexport interface UserData {\n // Hashable fields (SHA-256)\n email?: string;\n external_id?: string;\n ip_address?: string;\n user_agent?: string;\n idfa?: string;\n aaid?: string;\n\n // Pass-through fields (not hashed)\n uuid?: string;\n opt_out?: boolean;\n screen_dimensions?: ScreenDimensions;\n data_processing_options?: DataProcessingOptions;\n}\n\nexport interface ConversionEvent {\n click_id?: string;\n event_at: string;\n event_at_ms?: number;\n event_type: EventType;\n event_metadata?: EventMetadata;\n user: UserData;\n}\n\nexport interface RequestBody {\n test_mode?: boolean;\n data: {\n events: ConversionEvent[];\n };\n}\n\nexport interface ResponseBody {\n success?: boolean;\n message?: string;\n}\n","import type { Destination } from './types';\nimport { getConfig } from './config';\nimport { push } from './push';\n\n// Types\nexport * as DestinationReddit from './types';\n\nexport const destinationReddit: Destination = {\n type: 'reddit',\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 destinationReddit;\n"],"mappings":"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","names":["isObject","isObject"]}
1
+ {"version":3,"sources":["../src/config.ts","../src/push.ts","../src/hash.ts","../src/types/index.ts","../src/index.ts"],"sourcesContent":["import type { Config, Settings, PartialConfig } from './types';\nimport type { Logger } from '@walkeros/core';\n\nexport function getConfig(\n partialConfig: PartialConfig = {},\n logger: Logger.Instance,\n): Config {\n const settings = (partialConfig.settings || {}) as Partial<Settings>;\n const { accessToken, pixelId } = settings;\n\n if (!accessToken) logger.throw('Config settings accessToken missing');\n if (!pixelId) logger.throw('Config settings pixelId missing');\n\n const settingsConfig: Settings = {\n ...settings,\n accessToken,\n pixelId,\n };\n\n return { ...partialConfig, settings: settingsConfig };\n}\n","import type {\n ConversionEvent,\n EventType,\n PushFn,\n RequestBody,\n TrackingType,\n UserData,\n} from './types';\nimport type { Collector, Mapping as WalkerOSMapping } from '@walkeros/core';\nimport { createMappingRoot, getMappingValue, isObject } from '@walkeros/core';\nimport { sendServer } from '@walkeros/server-core';\nimport { hashEvent } from './hash';\n\nconst STANDARD_TRACKING_TYPES: ReadonlySet<TrackingType> =\n new Set<TrackingType>([\n 'PageVisit',\n 'ViewContent',\n 'Search',\n 'AddToCart',\n 'AddToWishlist',\n 'Purchase',\n 'Lead',\n 'SignUp',\n 'Custom',\n ]);\n\nfunction isStandardTrackingType(name: string): name is TrackingType {\n return STANDARD_TRACKING_TYPES.has(name as TrackingType);\n}\n\nfunction buildEventType(name: string): EventType {\n if (isStandardTrackingType(name) && name !== 'Custom') {\n return { tracking_type: name };\n }\n return { tracking_type: 'Custom', custom_event_name: name };\n}\n\nexport const push: PushFn = async function (\n event,\n { config, data, ingest, env, logger, collector },\n) {\n const {\n accessToken,\n pixelId,\n action_source: _action_source,\n doNotHash,\n test_mode,\n url = 'https://ads-api.reddit.com/api/v2.0/conversions/events/',\n user_data,\n ip,\n userAgent,\n } = config.settings!;\n\n const eventData = isObject(data) ? data : {};\n const configData = config.data\n ? await getMappingValue(event, config.data, { collector })\n : {};\n const userDataCustom = user_data\n ? await getMappingValue(event, { map: user_data }, { collector })\n : {};\n\n // Merge user data from config.data, settings.user_data, and event mapping\n // Client IP and user agent auto-fill, overridden by any mapped value\n const root = createMappingRoot(ingest, event);\n const clientIp = await resolveClient(ip, DEFAULT_IP, root, collector);\n const clientUserAgent = await resolveClient(\n userAgent,\n DEFAULT_USER_AGENT,\n root,\n collector,\n );\n\n const userData: UserData = {\n ...(clientIp !== undefined ? { ip_address: clientIp } : {}),\n ...(clientUserAgent !== undefined ? { user_agent: clientUserAgent } : {}),\n ...(isObject(configData) && isObject(configData.user)\n ? configData.user\n : {}),\n ...(isObject(userDataCustom) ? userDataCustom : {}),\n ...(isObject(eventData.user) ? eventData.user : {}),\n };\n\n const {\n user: _u,\n event_metadata: eventMetadata,\n click_id: eventClickId,\n ...restEventData\n } = eventData;\n\n const timestampMs = event.timestamp || Date.now();\n\n const serverEvent: ConversionEvent = {\n event_at: new Date(timestampMs).toISOString(),\n event_at_ms: timestampMs,\n event_type: buildEventType(event.name),\n ...restEventData,\n user: userData,\n };\n\n // Merge event_metadata with auto-populated conversion_id (event.id for dedup)\n const metadataFromMapping = isObject(eventMetadata) ? eventMetadata : {};\n serverEvent.event_metadata = {\n conversion_id: event.id,\n ...metadataFromMapping,\n };\n\n if (typeof eventClickId === 'string') {\n serverEvent.click_id = eventClickId;\n }\n\n const hashedServerEvent = await hashEvent(serverEvent, doNotHash);\n\n const body: RequestBody = {\n ...(test_mode ? { test_mode: true } : {}),\n data: { events: [hashedServerEvent] },\n };\n\n const endpoint = `${url}${pixelId}`;\n\n logger.debug('Calling Reddit API', {\n endpoint,\n method: 'POST',\n trackingType: serverEvent.event_type.tracking_type,\n eventId: serverEvent.event_metadata?.conversion_id,\n });\n\n const sendServerFn = env?.sendServer || sendServer;\n const result = await sendServerFn(endpoint, JSON.stringify(body), {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n logger.debug('Reddit API response', {\n ok: isObject(result) ? result.ok : true,\n });\n\n if (isObject(result) && result.ok === false) {\n logger.throw(`Reddit API error: ${JSON.stringify(result)}`);\n }\n};\n\nconst DEFAULT_IP: WalkerOSMapping.Value = ['ingest.ip', 'event.user.ip'];\nconst DEFAULT_USER_AGENT: WalkerOSMapping.Value = [\n 'ingest.userAgent',\n 'event.user.userAgent',\n];\n\n/** A client IP or user agent setting resolved to a non-empty string. */\nasync function resolveClient(\n setting: WalkerOSMapping.Value | false | undefined,\n fallback: WalkerOSMapping.Value,\n root: WalkerOSMapping.Root,\n collector: Collector.Instance,\n): Promise<string | undefined> {\n if (setting === false) return undefined;\n const value = await getMappingValue(root, setting ?? fallback, { collector });\n return typeof value === 'string' && value !== '' ? value : undefined;\n}\n","import { WalkerOS } from '@walkeros/core';\nimport { isArray, isObject, isString } from '@walkeros/core';\nimport { getHashServer } from '@walkeros/server-core';\n\nconst keysToHash = [\n 'email',\n 'external_id',\n 'ip_address',\n 'user_agent',\n 'idfa',\n 'aaid',\n];\n\nfunction shouldBeHashed(key: string, doNotHash: string[] = []): boolean {\n return keysToHash.includes(key) && !doNotHash.includes(key);\n}\n\ntype HashableValue = WalkerOS.AnyObject | unknown | unknown[];\n\nasync function processValue(\n value: unknown,\n shouldHash: boolean,\n): Promise<unknown> {\n if (!shouldHash) return value;\n if (isArray(value)) {\n return Promise.all(value.map((item) => getHashServer(String(item))));\n }\n return getHashServer(String(value));\n}\n\nexport async function hashEvent<T extends HashableValue>(\n value: T,\n doNotHash: string[] = [],\n): Promise<T> {\n if (!isObject(value)) return value;\n\n const isUser = 'user' in value;\n const target = (isUser ? value.user : value) as WalkerOS.AnyObject;\n\n const entries = await Promise.all(\n Object.entries(target).map(async ([k, v]) => [\n k,\n await processValue(v, isUser && shouldBeHashed(k, doNotHash)),\n ]),\n );\n\n const result = entries.reduce((acc, [k, v]) => {\n if (isString(k)) acc[k] = v;\n return acc;\n }, {} as WalkerOS.AnyObject);\n\n return isUser ? { ...value, user: result } : (result as T);\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 accessToken: string;\n pixelId: string;\n action_source?: ActionSource;\n doNotHash?: string[];\n test_mode?: boolean;\n url?: string;\n user_data?: WalkerOSMapping.Map;\n /**\n * Client IP, sent as user.ip_address (hashed). Resolves against { ingest, event }.\n * `false` switches it off.\n * @default ['ingest.ip', 'event.user.ip']\n */\n ip?: WalkerOSMapping.Value | false;\n /**\n * Client user agent, sent as user.user_agent (hashed). Resolves against\n * { ingest, event }. `false` switches it off.\n * @default ['ingest.userAgent', 'event.user.userAgent']\n */\n userAgent?: WalkerOSMapping.Value | false;\n}\n\nexport type InitSettings = Partial<Settings>;\n\nexport interface Mapping {}\n\nexport interface Env extends DestinationServer.Env {\n sendServer?: typeof sendServer;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport interface Destination extends DestinationServer.Destination<Types> {\n init: DestinationServer.InitFn<Types>;\n}\n\nexport type Config = {\n settings: Settings;\n} & DestinationServer.Config<Types>;\n\nexport type InitFn = DestinationServer.InitFn<Types>;\nexport type PushFn = DestinationServer.PushFn<Types>;\n\nexport type PartialConfig = DestinationServer.PartialConfig<Types>;\n\nexport type PushEvents = DestinationServer.PushEvents<Mapping>;\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n\n// Reddit Conversions API types\n// https://ads-api.reddit.com/docs/v2/#tag/Conversions-API\n\nexport type ActionSource = 'WEBSITE' | 'APP' | 'PHYSICAL_STORE';\n\nexport type TrackingType =\n | 'PageVisit'\n | 'ViewContent'\n | 'Search'\n | 'AddToCart'\n | 'AddToWishlist'\n | 'Purchase'\n | 'Lead'\n | 'SignUp'\n | 'Custom';\n\nexport interface EventType {\n tracking_type: TrackingType;\n custom_event_name?: string;\n}\n\nexport interface Product {\n id: string;\n name?: string;\n category: string;\n}\n\nexport interface EventMetadata {\n conversion_id?: string;\n item_count?: number;\n currency?: string;\n value?: number;\n value_decimal?: number;\n products?: Product[];\n units_sold?: number;\n country_code?: string;\n}\n\nexport interface ScreenDimensions {\n width: number;\n height: number;\n}\n\nexport interface DataProcessingOptions {\n modes: string[];\n country?: string;\n region?: string;\n}\n\nexport interface UserData {\n // Hashable fields (SHA-256)\n email?: string;\n external_id?: string;\n ip_address?: string;\n user_agent?: string;\n idfa?: string;\n aaid?: string;\n\n // Pass-through fields (not hashed)\n uuid?: string;\n opt_out?: boolean;\n screen_dimensions?: ScreenDimensions;\n data_processing_options?: DataProcessingOptions;\n}\n\nexport interface ConversionEvent {\n click_id?: string;\n event_at: string;\n event_at_ms?: number;\n event_type: EventType;\n event_metadata?: EventMetadata;\n user: UserData;\n}\n\nexport interface RequestBody {\n test_mode?: boolean;\n data: {\n events: ConversionEvent[];\n };\n}\n\nexport interface ResponseBody {\n success?: boolean;\n message?: string;\n}\n","import type { Destination } from './types';\nimport { getConfig } from './config';\nimport { push } from './push';\n\n// Types\nexport * as DestinationReddit from './types';\n\nexport const destinationReddit: Destination = {\n type: 'reddit',\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 destinationReddit;\n"],"mappings":";AAGO,SAAS,UACd,gBAA+B,CAAC,GAChC,QACQ;AACR,QAAM,WAAY,cAAc,YAAY,CAAC;AAC7C,QAAM,EAAE,aAAa,QAAQ,IAAI;AAEjC,MAAI,CAAC,YAAa,QAAO,MAAM,qCAAqC;AACpE,MAAI,CAAC,QAAS,QAAO,MAAM,iCAAiC;AAE5D,QAAM,iBAA2B;AAAA,IAC/B,GAAG;AAAA,IACH;AAAA,IACA;AAAA,EACF;AAEA,SAAO,EAAE,GAAG,eAAe,UAAU,eAAe;AACtD;;;ACXA,SAAS,mBAAmB,iBAAiB,YAAAA,iBAAgB;AAC7D,SAAS,kBAAkB;;;ACT3B,SAAS,SAAS,UAAU,gBAAgB;AAC5C,SAAS,qBAAqB;AAE9B,IAAM,aAAa;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,eAAe,KAAa,YAAsB,CAAC,GAAY;AACtE,SAAO,WAAW,SAAS,GAAG,KAAK,CAAC,UAAU,SAAS,GAAG;AAC5D;AAIA,eAAe,aACb,OACA,YACkB;AAClB,MAAI,CAAC,WAAY,QAAO;AACxB,MAAI,QAAQ,KAAK,GAAG;AAClB,WAAO,QAAQ,IAAI,MAAM,IAAI,CAAC,SAAS,cAAc,OAAO,IAAI,CAAC,CAAC,CAAC;AAAA,EACrE;AACA,SAAO,cAAc,OAAO,KAAK,CAAC;AACpC;AAEA,eAAsB,UACpB,OACA,YAAsB,CAAC,GACX;AACZ,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAE7B,QAAM,SAAS,UAAU;AACzB,QAAM,SAAU,SAAS,MAAM,OAAO;AAEtC,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,OAAO,QAAQ,MAAM,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM;AAAA,MAC3C;AAAA,MACA,MAAM,aAAa,GAAG,UAAU,eAAe,GAAG,SAAS,CAAC;AAAA,IAC9D,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,QAAQ,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM;AAC7C,QAAI,SAAS,CAAC,EAAG,KAAI,CAAC,IAAI;AAC1B,WAAO;AAAA,EACT,GAAG,CAAC,CAAuB;AAE3B,SAAO,SAAS,EAAE,GAAG,OAAO,MAAM,OAAO,IAAK;AAChD;;;ADvCA,IAAM,0BACJ,oBAAI,IAAkB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEH,SAAS,uBAAuB,MAAoC;AAClE,SAAO,wBAAwB,IAAI,IAAoB;AACzD;AAEA,SAAS,eAAe,MAAyB;AAC/C,MAAI,uBAAuB,IAAI,KAAK,SAAS,UAAU;AACrD,WAAO,EAAE,eAAe,KAAK;AAAA,EAC/B;AACA,SAAO,EAAE,eAAe,UAAU,mBAAmB,KAAK;AAC5D;AAEO,IAAM,OAAe,eAC1B,OACA,EAAE,QAAQ,MAAM,QAAQ,KAAK,QAAQ,UAAU,GAC/C;AACA,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,OAAO;AAEX,QAAM,YAAYC,UAAS,IAAI,IAAI,OAAO,CAAC;AAC3C,QAAM,aAAa,OAAO,OACtB,MAAM,gBAAgB,OAAO,OAAO,MAAM,EAAE,UAAU,CAAC,IACvD,CAAC;AACL,QAAM,iBAAiB,YACnB,MAAM,gBAAgB,OAAO,EAAE,KAAK,UAAU,GAAG,EAAE,UAAU,CAAC,IAC9D,CAAC;AAIL,QAAM,OAAO,kBAAkB,QAAQ,KAAK;AAC5C,QAAM,WAAW,MAAM,cAAc,IAAI,YAAY,MAAM,SAAS;AACpE,QAAM,kBAAkB,MAAM;AAAA,IAC5B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,WAAqB;AAAA,IACzB,GAAI,aAAa,SAAY,EAAE,YAAY,SAAS,IAAI,CAAC;AAAA,IACzD,GAAI,oBAAoB,SAAY,EAAE,YAAY,gBAAgB,IAAI,CAAC;AAAA,IACvE,GAAIA,UAAS,UAAU,KAAKA,UAAS,WAAW,IAAI,IAChD,WAAW,OACX,CAAC;AAAA,IACL,GAAIA,UAAS,cAAc,IAAI,iBAAiB,CAAC;AAAA,IACjD,GAAIA,UAAS,UAAU,IAAI,IAAI,UAAU,OAAO,CAAC;AAAA,EACnD;AAEA,QAAM;AAAA,IACJ,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,UAAU;AAAA,IACV,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,cAAc,MAAM,aAAa,KAAK,IAAI;AAEhD,QAAM,cAA+B;AAAA,IACnC,UAAU,IAAI,KAAK,WAAW,EAAE,YAAY;AAAA,IAC5C,aAAa;AAAA,IACb,YAAY,eAAe,MAAM,IAAI;AAAA,IACrC,GAAG;AAAA,IACH,MAAM;AAAA,EACR;AAGA,QAAM,sBAAsBA,UAAS,aAAa,IAAI,gBAAgB,CAAC;AACvE,cAAY,iBAAiB;AAAA,IAC3B,eAAe,MAAM;AAAA,IACrB,GAAG;AAAA,EACL;AAEA,MAAI,OAAO,iBAAiB,UAAU;AACpC,gBAAY,WAAW;AAAA,EACzB;AAEA,QAAM,oBAAoB,MAAM,UAAU,aAAa,SAAS;AAEhE,QAAM,OAAoB;AAAA,IACxB,GAAI,YAAY,EAAE,WAAW,KAAK,IAAI,CAAC;AAAA,IACvC,MAAM,EAAE,QAAQ,CAAC,iBAAiB,EAAE;AAAA,EACtC;AAEA,QAAM,WAAW,GAAG,GAAG,GAAG,OAAO;AAEjC,SAAO,MAAM,sBAAsB;AAAA,IACjC;AAAA,IACA,QAAQ;AAAA,IACR,cAAc,YAAY,WAAW;AAAA,IACrC,SAAS,YAAY,gBAAgB;AAAA,EACvC,CAAC;AAED,QAAM,eAAe,KAAK,cAAc;AACxC,QAAM,SAAS,MAAM,aAAa,UAAU,KAAK,UAAU,IAAI,GAAG;AAAA,IAChE,SAAS,EAAE,eAAe,UAAU,WAAW,GAAG;AAAA,EACpD,CAAC;AAED,SAAO,MAAM,uBAAuB;AAAA,IAClC,IAAIA,UAAS,MAAM,IAAI,OAAO,KAAK;AAAA,EACrC,CAAC;AAED,MAAIA,UAAS,MAAM,KAAK,OAAO,OAAO,OAAO;AAC3C,WAAO,MAAM,qBAAqB,KAAK,UAAU,MAAM,CAAC,EAAE;AAAA,EAC5D;AACF;AAEA,IAAM,aAAoC,CAAC,aAAa,eAAe;AACvE,IAAM,qBAA4C;AAAA,EAChD;AAAA,EACA;AACF;AAGA,eAAe,cACb,SACA,UACA,MACA,WAC6B;AAC7B,MAAI,YAAY,MAAO,QAAO;AAC9B,QAAM,QAAQ,MAAM,gBAAgB,MAAM,WAAW,UAAU,EAAE,UAAU,CAAC;AAC5E,SAAO,OAAO,UAAU,YAAY,UAAU,KAAK,QAAQ;AAC7D;;;AE5JA;;;ACOO,IAAM,oBAAiC;AAAA,EAC5C,MAAM;AAAA,EAEN,QAAQ,CAAC;AAAA,EAET,MAAM,KAAK,EAAE,QAAQ,eAAe,OAAO,GAAG;AAC5C,UAAM,SAAS,UAAU,eAAe,MAAM;AAC9C,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,OAAO,SAAS;AACzB,WAAO,MAAM,KAAK,OAAO,OAAO;AAAA,EAClC;AACF;AAEA,IAAO,gBAAQ;","names":["isObject","isObject"]}
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "$meta": {
3
3
  "package": "@walkeros/server-destination-reddit",
4
- "version": "4.7.0-next-1790245733116",
4
+ "version": "4.7.0",
5
5
  "type": "destination",
6
6
  "platform": [
7
7
  "server"
8
8
  ],
9
+ "exports": {},
9
10
  "docs": "https://www.walkeros.io/docs/destinations/server/reddit",
10
11
  "source": "https://github.com/elbwalker/walkerOS/tree/main/packages/server/destinations/reddit/src"
11
12
  },
@@ -64,13 +65,187 @@
64
65
  "type": "string"
65
66
  },
66
67
  "description": "Mapping configuration for user fields (like { email: 'user.email', external_id: 'user.id' })"
68
+ },
69
+ "ip": {
70
+ "anyOf": [
71
+ {
72
+ "$ref": "#/definitions/MappingValue"
73
+ },
74
+ {
75
+ "type": "boolean",
76
+ "const": false
77
+ }
78
+ ],
79
+ "description": "Client IP, sent as user.ip_address (hashed). Resolves against { ingest, event }. Default: [\"ingest.ip\", \"event.user.ip\"]. false switches it off. A mapped user.ip_address wins."
80
+ },
81
+ "userAgent": {
82
+ "anyOf": [
83
+ {
84
+ "$ref": "#/definitions/MappingValue"
85
+ },
86
+ {
87
+ "type": "boolean",
88
+ "const": false
89
+ }
90
+ ],
91
+ "description": "Client user agent, sent as user.user_agent (hashed). Resolves against { ingest, event }. Default: [\"ingest.userAgent\", \"event.user.userAgent\"]. false switches it off. A mapped user.user_agent wins."
67
92
  }
68
93
  },
69
94
  "required": [
70
95
  "accessToken",
71
96
  "pixelId"
72
97
  ],
73
- "additionalProperties": false
98
+ "additionalProperties": false,
99
+ "definitions": {
100
+ "MappingValue": {
101
+ "title": "Mapping.Value",
102
+ "description": "Polymorphic transform primitive used in every mapping field. A string path, constant, operator object (map/loop/set/condition/consent), or array of values.",
103
+ "anyOf": [
104
+ {
105
+ "type": "string",
106
+ "description": "String value or property path (e.g., \"data.id\")"
107
+ },
108
+ {
109
+ "type": "number",
110
+ "description": "Numeric value"
111
+ },
112
+ {
113
+ "type": "boolean",
114
+ "description": "Boolean value"
115
+ },
116
+ {
117
+ "description": "Value transformation configuration with multiple strategies",
118
+ "allOf": [
119
+ {
120
+ "$ref": "#/definitions/MappingValueConfig"
121
+ }
122
+ ]
123
+ },
124
+ {
125
+ "type": "array",
126
+ "items": {
127
+ "$ref": "#/definitions/MappingValue"
128
+ },
129
+ "description": "Array of values"
130
+ }
131
+ ]
132
+ },
133
+ "MappingMap": {
134
+ "title": "Mapping.Map",
135
+ "description": "Map: object mapping keys to transformation values.",
136
+ "type": "object",
137
+ "propertyNames": {
138
+ "type": "string"
139
+ },
140
+ "additionalProperties": {
141
+ "$ref": "#/definitions/MappingValue"
142
+ }
143
+ },
144
+ "MappingLoop": {
145
+ "title": "Mapping.Loop",
146
+ "description": "Loop tuple [source, transform] for iterating and transforming arrays.",
147
+ "minItems": 2,
148
+ "maxItems": 2,
149
+ "type": "array",
150
+ "items": [
151
+ {
152
+ "$ref": "#/definitions/MappingValue"
153
+ },
154
+ {
155
+ "$ref": "#/definitions/MappingValue"
156
+ }
157
+ ]
158
+ },
159
+ "MappingSet": {
160
+ "title": "Mapping.Set",
161
+ "description": "Set: array of values for selection or combination.",
162
+ "type": "array",
163
+ "items": {
164
+ "$ref": "#/definitions/MappingValue"
165
+ }
166
+ },
167
+ "WalkerOSConsent": {
168
+ "type": "object",
169
+ "propertyNames": {
170
+ "type": "string"
171
+ },
172
+ "additionalProperties": {
173
+ "type": "boolean"
174
+ },
175
+ "title": "WalkerOS.Consent",
176
+ "description": "Consent state mapping. Keys are consent groups (e.g. marketing, functional), values are booleans for granted/denied."
177
+ },
178
+ "MappingValueConfig": {
179
+ "type": "object",
180
+ "properties": {
181
+ "key": {
182
+ "description": "Property path to extract from event (e.g., \"data.id\", \"user.email\")",
183
+ "type": "string"
184
+ },
185
+ "value": {
186
+ "description": "Static primitive value",
187
+ "anyOf": [
188
+ {
189
+ "type": "string"
190
+ },
191
+ {
192
+ "type": "number"
193
+ },
194
+ {
195
+ "type": "boolean"
196
+ }
197
+ ]
198
+ },
199
+ "fn": {
200
+ "description": "Custom transformation function as string (serialized)",
201
+ "type": "string"
202
+ },
203
+ "map": {
204
+ "description": "Object mapping: transform event data to structured output",
205
+ "allOf": [
206
+ {
207
+ "$ref": "#/definitions/MappingMap"
208
+ }
209
+ ]
210
+ },
211
+ "loop": {
212
+ "description": "Loop transformation: [source, transform] for array processing",
213
+ "allOf": [
214
+ {
215
+ "$ref": "#/definitions/MappingLoop"
216
+ }
217
+ ]
218
+ },
219
+ "set": {
220
+ "description": "Set of values: combine or select from multiple values",
221
+ "allOf": [
222
+ {
223
+ "$ref": "#/definitions/MappingSet"
224
+ }
225
+ ]
226
+ },
227
+ "consent": {
228
+ "description": "Required consent states to include this value",
229
+ "allOf": [
230
+ {
231
+ "$ref": "#/definitions/WalkerOSConsent"
232
+ }
233
+ ]
234
+ },
235
+ "condition": {
236
+ "description": "Condition function as string: return true to include value",
237
+ "type": "string"
238
+ },
239
+ "validate": {
240
+ "description": "Validation function as string: return true if value is valid",
241
+ "type": "string"
242
+ }
243
+ },
244
+ "additionalProperties": false,
245
+ "title": "Mapping.ValueConfig",
246
+ "description": "Object-form value transformation with map/loop/set/condition/consent etc."
247
+ }
248
+ }
74
249
  }
75
250
  },
76
251
  "examples": {
@@ -391,7 +566,7 @@
391
566
  "nested",
392
567
  {
393
568
  "condition": {
394
- "$code": "e=>h(e)&&\"product\"===e.entity"
569
+ "$code": "e=>b(e)&&\"product\"===e.entity"
395
570
  },
396
571
  "map": {
397
572
  "id": "data.id",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@walkeros/server-destination-reddit",
3
3
  "description": "Reddit server destination for walkerOS",
4
- "version": "4.7.0-next-1790245733116",
4
+ "version": "4.7.0",
5
5
  "license": "MIT",
6
6
  "exports": {
7
7
  ".": {
@@ -35,11 +35,11 @@
35
35
  "update": "npx npm-check-updates -u && npm update"
36
36
  },
37
37
  "dependencies": {
38
- "@walkeros/core": "4.7.0-next-1790245733116",
39
- "@walkeros/server-core": "4.7.0-next-1790245733116"
38
+ "@walkeros/core": "4.7.0",
39
+ "@walkeros/server-core": "4.7.0"
40
40
  },
41
41
  "devDependencies": {
42
- "@walkeros/collector": "4.7.0-next-1790245733116"
42
+ "@walkeros/collector": "4.7.0"
43
43
  },
44
44
  "repository": {
45
45
  "url": "git+https://github.com/elbwalker/walkerOS.git",