@walkeros/server-destination-datamanager 0.3.1 → 0.4.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/dist/dev.d.mts +165 -0
- package/dist/dev.d.ts +165 -0
- package/dist/dev.js +1 -0
- package/dist/dev.js.map +1 -0
- package/dist/dev.mjs +1 -0
- package/dist/dev.mjs.map +1 -0
- package/dist/index.d.mts +34 -111
- package/dist/index.d.ts +34 -111
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/schemas.d.mts +1 -1
- package/dist/schemas.d.ts +1 -1
- package/dist/schemas.js +1 -1
- package/dist/schemas.js.map +1 -1
- package/dist/schemas.mjs +1 -1
- package/dist/schemas.mjs.map +1 -1
- package/package.json +7 -7
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Destination as Destination$1, Mapping as Mapping$1
|
|
1
|
+
import { Destination as Destination$1, Mapping as Mapping$1 } from '@walkeros/core';
|
|
2
2
|
import { DestinationServer } from '@walkeros/server-core';
|
|
3
3
|
|
|
4
4
|
type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';
|
|
@@ -255,118 +255,41 @@ interface RequestError {
|
|
|
255
255
|
eventCount?: number;
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
type
|
|
259
|
-
type
|
|
260
|
-
type
|
|
261
|
-
type
|
|
262
|
-
type
|
|
263
|
-
type
|
|
264
|
-
type
|
|
265
|
-
type
|
|
266
|
-
type
|
|
267
|
-
type
|
|
268
|
-
type
|
|
269
|
-
type
|
|
270
|
-
type
|
|
271
|
-
type
|
|
272
|
-
type
|
|
273
|
-
type
|
|
274
|
-
type
|
|
275
|
-
type
|
|
276
|
-
type
|
|
277
|
-
type
|
|
278
|
-
type
|
|
279
|
-
type
|
|
280
|
-
type
|
|
281
|
-
type
|
|
282
|
-
type
|
|
283
|
-
type
|
|
284
|
-
type
|
|
285
|
-
type
|
|
286
|
-
type
|
|
287
|
-
type
|
|
288
|
-
type
|
|
289
|
-
declare namespace index$1 {
|
|
290
|
-
export type { index$1_AccountType as AccountType, index$1_AdIdentifiers as AdIdentifiers, index$1_Address as Address, index$1_CartData as CartData, index$1_CartItem as CartItem, index$1_Config as Config, index$1_Consent as Consent, index$1_ConsentStatus as ConsentStatus, index$1_Destination as Destination, index$1_DestinationInterface as DestinationInterface, index$1_Env as Env, index$1_Event as Event, index$1_EventSource as EventSource, index$1_IngestEventsRequest as IngestEventsRequest, index$1_IngestEventsResponse as IngestEventsResponse, index$1_InitFn as InitFn, index$1_Mapping as Mapping, index$1_OperatingAccount as OperatingAccount, index$1_PartialConfig as PartialConfig, index$1_PushEvents as PushEvents, index$1_PushFn as PushFn, index$1_RequestError as RequestError, index$1_RequestState as RequestState, index$1_RequestStatusResponse as RequestStatusResponse, index$1_Rule as Rule, index$1_Rules as Rules, index$1_Settings as Settings, index$1_Types as Types, index$1_UserData as UserData, index$1_UserIdentifier as UserIdentifier, index$1_ValidationError as ValidationError };
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Purchase event mapping for Google Ads conversion
|
|
295
|
-
*/
|
|
296
|
-
declare const Purchase: Rule;
|
|
297
|
-
/**
|
|
298
|
-
* Lead event mapping
|
|
299
|
-
*/
|
|
300
|
-
declare const Lead: Rule;
|
|
301
|
-
/**
|
|
302
|
-
* Page view mapping for GA4
|
|
303
|
-
*/
|
|
304
|
-
declare const PageView: Rule;
|
|
305
|
-
/**
|
|
306
|
-
* Complete mapping configuration
|
|
307
|
-
*/
|
|
308
|
-
declare const mapping: {
|
|
309
|
-
order: {
|
|
310
|
-
complete: Rule;
|
|
311
|
-
};
|
|
312
|
-
lead: {
|
|
313
|
-
submit: Rule;
|
|
314
|
-
};
|
|
315
|
-
page: {
|
|
316
|
-
view: Rule;
|
|
317
|
-
};
|
|
318
|
-
};
|
|
319
|
-
/**
|
|
320
|
-
* User data mapping configuration
|
|
321
|
-
* Maps walkerOS user properties to Data Manager user identifiers
|
|
322
|
-
*/
|
|
323
|
-
declare const userDataMapping: Config;
|
|
324
|
-
|
|
325
|
-
declare const mapping$1_Lead: typeof Lead;
|
|
326
|
-
declare const mapping$1_PageView: typeof PageView;
|
|
327
|
-
declare const mapping$1_Purchase: typeof Purchase;
|
|
328
|
-
declare const mapping$1_mapping: typeof mapping;
|
|
329
|
-
declare const mapping$1_userDataMapping: typeof userDataMapping;
|
|
330
|
-
declare namespace mapping$1 {
|
|
331
|
-
export { mapping$1_Lead as Lead, mapping$1_PageView as PageView, mapping$1_Purchase as Purchase, mapping$1_mapping as mapping, mapping$1_userDataMapping as userDataMapping };
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Minimal configuration for Google Data Manager
|
|
336
|
-
*/
|
|
337
|
-
declare const minimal: Config;
|
|
338
|
-
/**
|
|
339
|
-
* Complete configuration with all options
|
|
340
|
-
*/
|
|
341
|
-
declare const complete: Config;
|
|
342
|
-
/**
|
|
343
|
-
* GA4-specific configuration
|
|
344
|
-
*/
|
|
345
|
-
declare const ga4: Config;
|
|
346
|
-
/**
|
|
347
|
-
* Debug configuration with logging enabled
|
|
348
|
-
*/
|
|
349
|
-
declare const debug: Config;
|
|
350
|
-
|
|
351
|
-
declare const basic_complete: typeof complete;
|
|
352
|
-
declare const basic_debug: typeof debug;
|
|
353
|
-
declare const basic_ga4: typeof ga4;
|
|
354
|
-
declare const basic_minimal: typeof minimal;
|
|
355
|
-
declare namespace basic {
|
|
356
|
-
export { basic_complete as complete, basic_debug as debug, basic_ga4 as ga4, basic_minimal as minimal };
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
declare const index_basic: typeof basic;
|
|
258
|
+
type index_AccountType = AccountType;
|
|
259
|
+
type index_AdIdentifiers = AdIdentifiers;
|
|
260
|
+
type index_Address = Address;
|
|
261
|
+
type index_CartData = CartData;
|
|
262
|
+
type index_CartItem = CartItem;
|
|
263
|
+
type index_Config = Config;
|
|
264
|
+
type index_Consent = Consent;
|
|
265
|
+
type index_ConsentStatus = ConsentStatus;
|
|
266
|
+
type index_Destination = Destination;
|
|
267
|
+
type index_DestinationInterface = DestinationInterface;
|
|
268
|
+
type index_Env = Env;
|
|
269
|
+
type index_Event = Event;
|
|
270
|
+
type index_EventSource = EventSource;
|
|
271
|
+
type index_IngestEventsRequest = IngestEventsRequest;
|
|
272
|
+
type index_IngestEventsResponse = IngestEventsResponse;
|
|
273
|
+
type index_InitFn = InitFn;
|
|
274
|
+
type index_Mapping = Mapping;
|
|
275
|
+
type index_OperatingAccount = OperatingAccount;
|
|
276
|
+
type index_PartialConfig = PartialConfig;
|
|
277
|
+
type index_PushEvents = PushEvents;
|
|
278
|
+
type index_PushFn = PushFn;
|
|
279
|
+
type index_RequestError = RequestError;
|
|
280
|
+
type index_RequestState = RequestState;
|
|
281
|
+
type index_RequestStatusResponse = RequestStatusResponse;
|
|
282
|
+
type index_Rule = Rule;
|
|
283
|
+
type index_Rules = Rules;
|
|
284
|
+
type index_Settings = Settings;
|
|
285
|
+
type index_Types = Types;
|
|
286
|
+
type index_UserData = UserData;
|
|
287
|
+
type index_UserIdentifier = UserIdentifier;
|
|
288
|
+
type index_ValidationError = ValidationError;
|
|
360
289
|
declare namespace index {
|
|
361
|
-
export {
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
declare const schemas$1: Record<string, JSONSchema>;
|
|
365
|
-
|
|
366
|
-
declare namespace schemas {
|
|
367
|
-
export { schemas$1 as schemas };
|
|
290
|
+
export type { index_AccountType as AccountType, index_AdIdentifiers as AdIdentifiers, index_Address as Address, index_CartData as CartData, index_CartItem as CartItem, index_Config as Config, index_Consent as Consent, index_ConsentStatus as ConsentStatus, index_Destination as Destination, index_DestinationInterface as DestinationInterface, index_Env as Env, index_Event as Event, index_EventSource as EventSource, index_IngestEventsRequest as IngestEventsRequest, index_IngestEventsResponse as IngestEventsResponse, index_InitFn as InitFn, index_Mapping as Mapping, index_OperatingAccount as OperatingAccount, index_PartialConfig as PartialConfig, index_PushEvents as PushEvents, index_PushFn as PushFn, index_RequestError as RequestError, index_RequestState as RequestState, index_RequestStatusResponse as RequestStatusResponse, index_Rule as Rule, index_Rules as Rules, index_Settings as Settings, index_Types as Types, index_UserData as UserData, index_UserIdentifier as UserIdentifier, index_ValidationError as ValidationError };
|
|
368
291
|
}
|
|
369
292
|
|
|
370
293
|
declare const destinationDataManager: DestinationInterface;
|
|
371
294
|
|
|
372
|
-
export { index
|
|
295
|
+
export { index as DestinationDataManager, destinationDataManager as default, destinationDataManager };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,a=Object.getOwnPropertyNames,i=Object.prototype.hasOwnProperty,o=(e,n)=>{for(var a in n)t(e,a,{get:n[a],enumerable:!0})},s={};o(s,{DestinationDataManager:()=>b,default:()=>U,destinationDataManager:()=>W,examples:()=>f,schemas:()=>k}),module.exports=(e=s,((e,o,s,r)=>{if(o&&"object"==typeof o||"function"==typeof o)for(let c of a(o))i.call(e,c)||c===s||t(e,c,{get:()=>o[c],enumerable:!(r=n(o,c))||r.enumerable});return e})(t({},"__esModule",{value:!0}),e));var r=require("@walkeros/core");var c=require("@walkeros/core"),d=require("@walkeros/core"),l=require("@walkeros/core"),u=require("@walkeros/server-core");async function g(e,t="given"){if(!(0,l.isString)(e)||!e)return"";let n=e.trim().toLowerCase();if("given"===t){const e=["mr.","mrs.","ms.","miss.","dr.","prof."];for(const t of e)if(n.startsWith(t)){n=n.substring(t.length).trim();break}}if("family"===t){const e=["jr.","sr.","iii","ii","iv","v"];for(const t of e){const e=` ${t}`;if(n.endsWith(e)){n=n.substring(0,n.length-e.length).trim();break}if(n.endsWith(t)){n=n.substring(0,n.length-t.length).trim();break}}}return(0,u.getHashServer)(n)}async function p(e){const t=[];if((0,d.isString)(e.email)&&e.email){const n=await async function(e){if(!(0,l.isString)(e)||!e)return"";let t=e.trim().toLowerCase();if(t.endsWith("@gmail.com")||t.endsWith("@googlemail.com")){const[e,n]=t.split("@");t=`${e.replace(/\./g,"")}@${n}`}return(0,u.getHashServer)(t)}(e.email);n&&t.push({emailAddress:n})}if((0,d.isString)(e.phone)&&e.phone){const n=await async function(e){if(!(0,l.isString)(e)||!e)return"";let t=e.trim();const n=t.startsWith("+");return t=t.replace(/\D/g,""),t=n||t.length>10?`+${t}`:`+1${t}`,(0,u.getHashServer)(t)}(e.phone);n&&t.push({phoneNumber:n})}if(e.firstName||e.lastName||e.regionCode||e.postalCode){const n={};(0,d.isString)(e.firstName)&&e.firstName&&(n.givenName=await g(e.firstName,"given")),(0,d.isString)(e.lastName)&&e.lastName&&(n.familyName=await g(e.lastName,"family")),(0,d.isString)(e.regionCode)&&e.regionCode&&(n.regionCode=e.regionCode.toUpperCase()),(0,d.isString)(e.postalCode)&&e.postalCode&&(n.postalCode=e.postalCode),Object.keys(n).length>0&&t.push({address:n})}if(0!==t.length)return{userIdentifiers:t.slice(0,10)}}async function m(e,t){const n={eventTimestamp:(a=e.timestamp,new Date(a).toISOString())};var a;const i=t||{};(0,d.isString)(i.transactionId)&&i.transactionId&&(n.transactionId=i.transactionId.substring(0,512)),(0,d.isString)(i.clientId)&&i.clientId&&(n.clientId=i.clientId.substring(0,255)),(0,d.isString)(i.userId)&&i.userId&&(n.userId=i.userId.substring(0,256));const o=await p(i);o&&(n.userData=o);const s=function(e){const t={};return(0,d.isString)(e.gclid)&&e.gclid&&(t.gclid=e.gclid),(0,d.isString)(e.gbraid)&&e.gbraid&&(t.gbraid=e.gbraid),(0,d.isString)(e.wbraid)&&e.wbraid&&(t.wbraid=e.wbraid),(0,d.isString)(e.sessionAttributes)&&e.sessionAttributes&&(t.sessionAttributes=e.sessionAttributes),Object.keys(t).length>0?t:void 0}(i);s&&(n.adIdentifiers=s),"number"==typeof i.conversionValue&&(n.conversionValue=i.conversionValue),(0,d.isString)(i.currency)&&i.currency&&(n.currency=i.currency.substring(0,3).toUpperCase()),i.cartData&&"object"==typeof i.cartData&&(n.cartData=i.cartData),(0,d.isString)(i.eventName)&&i.eventName&&(n.eventName=i.eventName.substring(0,40)),(0,d.isString)(i.eventSource)&&i.eventSource&&(n.eventSource=i.eventSource);const r={};if("boolean"==typeof i.adUserData&&(r.adUserData=i.adUserData?"CONSENT_GRANTED":"CONSENT_DENIED"),"boolean"==typeof i.adPersonalization&&(r.adPersonalization=i.adPersonalization?"CONSENT_GRANTED":"CONSENT_DENIED"),0===Object.keys(r).length){const t=function(e){if(!e)return;const t={};return(0,d.isDefined)(e.marketing)&&(t.adUserData=e.marketing?"CONSENT_GRANTED":"CONSENT_DENIED"),(0,d.isDefined)(e.personalization)&&(t.adPersonalization=e.personalization?"CONSENT_GRANTED":"CONSENT_DENIED"),Object.keys(t).length>0?t:void 0}(e.consent);t&&(n.consent=t)}else n.consent=r;return n}var v=async function(e,{config:t,mapping:n,data:a,collector:i,env:o}){const{accessToken:s,destinations:r,eventSource:d,validateOnly:l=!1,url:u="https://datamanager.googleapis.com/v1",consent:g,testEventCode:p,logLevel:v="none",userData:b,userId:f,clientId:N,sessionAttributes:y,consentAdUserData:I,consentAdPersonalization:D}=t.settings,S=function(e="none"){const t={debug:0,info:1,warn:2,error:3,none:4}[e];return{debug:(e,n)=>{t<=0&&console.log(`[DataManager] ${e}`,n||"")},info:(e,n)=>{t<=1&&console.log(`[DataManager] ${e}`,n||"")},warn:(e,n)=>{t<=2&&console.warn(`[DataManager] ${e}`,n||"")},error:(e,n)=>{t<=3&&console.error(`[DataManager] ${e}`,n||"")}}}(v),h=b?await(0,c.getMappingValue)(e,{map:b}):{},O=f?await(0,c.getMappingValue)(e,f):void 0,E=N?await(0,c.getMappingValue)(e,N):void 0,A=y?await(0,c.getMappingValue)(e,y):void 0,w="boolean"==typeof I?I:"string"==typeof I&&e.consent?e.consent[I]:void 0,T="boolean"==typeof D?D:"string"==typeof D&&e.consent?e.consent[D]:void 0,z={};(0,c.isObject)(h)&&Object.assign(z,h),void 0!==O&&(z.userId=O),void 0!==E&&(z.clientId=E),void 0!==A&&(z.sessionAttributes=A),void 0!==w&&(z.adUserData=w),void 0!==T&&(z.adPersonalization=T);const k=t.data?await(0,c.getMappingValue)(e,t.data):{},C=(0,c.isObject)(a)?a:{},P={...z,...(0,c.isObject)(k)?k:{},...C},_=await m(e,P);S.debug("Processing event",{name:e.name,id:e.id,timestamp:e.timestamp}),!_.eventSource&&d&&(_.eventSource=d),!_.consent&&g&&(_.consent=g);const x={events:[_],destinations:r};g&&(x.consent=g),l&&(x.validateOnly=!0),p&&(x.testEventCode=p);const G=(null==o?void 0:o.fetch)||fetch,R=`${u}/events:ingest`;S.debug("Sending to Data Manager API",{endpoint:R,eventCount:x.events.length,destinations:r.length,validateOnly:l});const j=await G(R,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify(x)});if(!j.ok){const e=await j.text();throw S.error("API request failed",{status:j.status,error:e}),new Error(`Data Manager API error (${j.status}): ${e}`)}const L=await j.json();if(S.debug("API response",{status:j.status,requestId:L.requestId}),L.validationErrors&&L.validationErrors.length>0)throw S.error("Validation errors",{errors:L.validationErrors}),new Error(`Validation errors: ${JSON.stringify(L.validationErrors)}`);S.info("Event processed successfully",{requestId:L.requestId})},b={},f={};o(f,{basic:()=>E,mapping:()=>N});var N={};o(N,{Lead:()=>D,PageView:()=>S,Purchase:()=>I,mapping:()=>h,userDataMapping:()=>O});var y=require("@walkeros/core"),I={name:"purchase",data:{map:{transactionId:"data.id",conversionValue:"data.total",currency:{key:"data.currency",value:"USD"},eventName:{value:"purchase"},userId:"user.id",email:"user.id",gclid:"context.gclid",gbraid:"context.gbraid",wbraid:"context.wbraid",cartData:{map:{items:{loop:["nested",{condition:e=>(0,y.isObject)(e)&&"product"===e.entity,map:{merchantProductId:"data.id",price:"data.price",quantity:{key:"data.quantity",value:1}}}]}}}}}},D={name:"generate_lead",data:{map:{eventName:{value:"generate_lead"},conversionValue:{value:10},currency:{value:"USD"}}}},S={name:"page_view",data:{map:{eventName:{value:"page_view"}}}},h={order:{complete:I},lead:{submit:D},page:{view:S}},O={settings:{accessToken:"ya29.c.xxx",destinations:[{operatingAccount:{accountId:"123-456-7890",accountType:"GOOGLE_ADS"},productDestinationId:"AW-CONVERSION-123"}]},data:{map:{email:"user.id",phone:"data.phone",firstName:"data.firstName",lastName:"data.lastName",regionCode:"data.country",postalCode:"data.zip"}},mapping:h},E={};o(E,{complete:()=>w,debug:()=>z,ga4:()=>T,minimal:()=>A});var A={settings:{accessToken:"ya29.c.xxx",destinations:[{operatingAccount:{accountId:"123-456-7890",accountType:"GOOGLE_ADS"},productDestinationId:"AW-CONVERSION-123"}]}},w={settings:{accessToken:"ya29.c.xxx",destinations:[{operatingAccount:{accountId:"123-456-7890",accountType:"GOOGLE_ADS"},productDestinationId:"AW-CONVERSION-123"},{operatingAccount:{accountId:"987654321",accountType:"GOOGLE_ANALYTICS_PROPERTY"},productDestinationId:"G-XXXXXXXXXX"}],eventSource:"WEB",batchSize:100,batchInterval:5e3,validateOnly:!1,consent:{adUserData:"CONSENT_GRANTED",adPersonalization:"CONSENT_GRANTED"},userData:{email:"user.id",phone:"data.phone",firstName:"data.firstName",lastName:"data.lastName"},userId:"user.id",clientId:"user.device",sessionAttributes:"context.sessionAttributes"},data:{map:{eventSource:{value:"WEB"}}}},T={settings:{accessToken:"ya29.c.xxx",destinations:[{operatingAccount:{accountId:"123456789",accountType:"GOOGLE_ANALYTICS_PROPERTY"},productDestinationId:"G-XXXXXXXXXX"}],eventSource:"WEB"}},z={settings:{accessToken:"ya29.c.xxx",destinations:[{operatingAccount:{accountId:"123-456-7890",accountType:"GOOGLE_ADS"},productDestinationId:"AW-CONVERSION-123"}],logLevel:"debug"}},k={};o(k,{schemas:()=>M});var C=require("@walkeros/core"),P=C.z.enum(["GOOGLE_ADS","DISPLAY_VIDEO_ADVERTISER","DISPLAY_VIDEO_PARTNER","GOOGLE_ANALYTICS_PROPERTY"]),_=C.z.enum(["WEB","APP","IN_STORE","PHONE","OTHER"]),x=C.z.enum(["CONSENT_GRANTED","CONSENT_DENIED"]),G=C.z.object({adUserData:x.describe("Consent for data collection and use").optional(),adPersonalization:x.describe("Consent for ad personalization").optional()}),R=C.z.object({accountId:C.z.string().min(1).describe('Account ID (e.g., "123-456-7890" for Google Ads)'),accountType:P.describe("Type of account")}),j=C.z.object({operatingAccount:R.describe("Operating account details"),productDestinationId:C.z.string().min(1).describe("Product-specific destination ID (conversion action or user list)")}),L=require("@walkeros/core"),V=L.z.object({accessToken:L.z.string().min(1).describe("OAuth 2.0 access token with datamanager scope (like ya29.c.xxx)"),destinations:L.z.array(j).min(1).max(10).describe("Array of destination accounts and conversion actions/user lists (max 10)"),eventSource:_.describe("Default event source if not specified per event (like WEB)").optional(),batchSize:L.z.number().int().min(1).max(2e3).describe("Maximum number of events to batch before sending (max 2000, like 100)").optional(),batchInterval:L.z.number().int().min(0).describe("Time in milliseconds to wait before auto-flushing batch (like 5000)").optional(),validateOnly:L.z.boolean().describe("If true, validate request without ingestion (testing mode)").optional(),url:L.z.string().url().describe("Override API endpoint for testing (like https://datamanager.googleapis.com/v1)").optional(),consent:G.describe("Request-level consent for all events").optional(),testEventCode:L.z.string().describe("Test event code for debugging (like TEST12345)").optional(),logLevel:L.z.enum(["debug","info","warn","error","none"]).describe("Log level for debugging (debug shows all API calls)").optional(),userData:L.z.record(L.z.string(),L.z.unknown()).describe("Guided helper: User data mapping for all events (like { email: 'user.id', phone: 'data.phone' })").optional(),userId:L.z.any().describe("Guided helper: First-party user ID for all events (like 'user.id')").optional(),clientId:L.z.any().describe("Guided helper: GA4 client ID for all events (like 'user.device')").optional(),sessionAttributes:L.z.any().describe("Guided helper: Privacy-safe attribution for all events (like 'context.sessionAttributes')").optional(),consentAdUserData:L.z.union([L.z.string(),L.z.boolean()]).describe("Consent mapping: Field name from event.consent (like 'marketing') or static boolean value").optional(),consentAdPersonalization:L.z.union([L.z.string(),L.z.boolean()]).describe("Consent mapping: Field name from event.consent (like 'targeting') or static boolean value").optional()}),X=require("@walkeros/core").z.object({}),q=require("@walkeros/core"),M={settings:(0,q.zodToSchema)(V),mapping:(0,q.zodToSchema)(X)},W={type:"datamanager",config:{},async init({config:e}){const t=function(e={}){const t=e.settings||{},{accessToken:n,destinations:a}=t;n||(0,r.throwError)("Config settings accessToken missing"),a&&0!==a.length||(0,r.throwError)("Config settings destinations missing or empty");const i={...t,accessToken:n,destinations:a};return{...e,settings:i}}(e);return t},push:async(e,{config:t,mapping:n,data:a,collector:i,env:o})=>await v(e,{config:t,mapping:n,data:a,collector:i,env:o})},U=W;//# sourceMappingURL=index.js.map
|
|
1
|
+
"use strict";var e,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.prototype.hasOwnProperty,a={};((e,n)=>{for(var r in n)t(e,r,{get:n[r],enumerable:!0})})(a,{DestinationDataManager:()=>m,default:()=>b,destinationDataManager:()=>v}),module.exports=(e=a,((e,a,s,o)=>{if(a&&"object"==typeof a||"function"==typeof a)for(let c of r(a))i.call(e,c)||c===s||t(e,c,{get:()=>a[c],enumerable:!(o=n(a,c))||o.enumerable});return e})(t({},"__esModule",{value:!0}),e));var s=require("@walkeros/core");var o=require("@walkeros/core"),c=require("@walkeros/core"),g=require("@walkeros/core"),d=require("@walkeros/server-core");async function l(e,t="given"){if(!(0,g.isString)(e)||!e)return"";let n=e.trim().toLowerCase();if("given"===t){const e=["mr.","mrs.","ms.","miss.","dr.","prof."];for(const t of e)if(n.startsWith(t)){n=n.substring(t.length).trim();break}}if("family"===t){const e=["jr.","sr.","iii","ii","iv","v"];for(const t of e){const e=` ${t}`;if(n.endsWith(e)){n=n.substring(0,n.length-e.length).trim();break}if(n.endsWith(t)){n=n.substring(0,n.length-t.length).trim();break}}}return(0,d.getHashServer)(n)}async function u(e){const t=[];if((0,c.isString)(e.email)&&e.email){const n=await async function(e){if(!(0,g.isString)(e)||!e)return"";let t=e.trim().toLowerCase();if(t.endsWith("@gmail.com")||t.endsWith("@googlemail.com")){const[e,n]=t.split("@");t=`${e.replace(/\./g,"")}@${n}`}return(0,d.getHashServer)(t)}(e.email);n&&t.push({emailAddress:n})}if((0,c.isString)(e.phone)&&e.phone){const n=await async function(e){if(!(0,g.isString)(e)||!e)return"";let t=e.trim();const n=t.startsWith("+");return t=t.replace(/\D/g,""),t=n||t.length>10?`+${t}`:`+1${t}`,(0,d.getHashServer)(t)}(e.phone);n&&t.push({phoneNumber:n})}if(e.firstName||e.lastName||e.regionCode||e.postalCode){const n={};(0,c.isString)(e.firstName)&&e.firstName&&(n.givenName=await l(e.firstName,"given")),(0,c.isString)(e.lastName)&&e.lastName&&(n.familyName=await l(e.lastName,"family")),(0,c.isString)(e.regionCode)&&e.regionCode&&(n.regionCode=e.regionCode.toUpperCase()),(0,c.isString)(e.postalCode)&&e.postalCode&&(n.postalCode=e.postalCode),Object.keys(n).length>0&&t.push({address:n})}if(0!==t.length)return{userIdentifiers:t.slice(0,10)}}async function f(e,t){const n={eventTimestamp:(r=e.timestamp,new Date(r).toISOString())};var r;const i=t||{};(0,c.isString)(i.transactionId)&&i.transactionId&&(n.transactionId=i.transactionId.substring(0,512)),(0,c.isString)(i.clientId)&&i.clientId&&(n.clientId=i.clientId.substring(0,255)),(0,c.isString)(i.userId)&&i.userId&&(n.userId=i.userId.substring(0,256));const a=await u(i);a&&(n.userData=a);const s=function(e){const t={};return(0,c.isString)(e.gclid)&&e.gclid&&(t.gclid=e.gclid),(0,c.isString)(e.gbraid)&&e.gbraid&&(t.gbraid=e.gbraid),(0,c.isString)(e.wbraid)&&e.wbraid&&(t.wbraid=e.wbraid),(0,c.isString)(e.sessionAttributes)&&e.sessionAttributes&&(t.sessionAttributes=e.sessionAttributes),Object.keys(t).length>0?t:void 0}(i);s&&(n.adIdentifiers=s),"number"==typeof i.conversionValue&&(n.conversionValue=i.conversionValue),(0,c.isString)(i.currency)&&i.currency&&(n.currency=i.currency.substring(0,3).toUpperCase()),i.cartData&&"object"==typeof i.cartData&&(n.cartData=i.cartData),(0,c.isString)(i.eventName)&&i.eventName&&(n.eventName=i.eventName.substring(0,40)),(0,c.isString)(i.eventSource)&&i.eventSource&&(n.eventSource=i.eventSource);const o={};if("boolean"==typeof i.adUserData&&(o.adUserData=i.adUserData?"CONSENT_GRANTED":"CONSENT_DENIED"),"boolean"==typeof i.adPersonalization&&(o.adPersonalization=i.adPersonalization?"CONSENT_GRANTED":"CONSENT_DENIED"),0===Object.keys(o).length){const t=function(e){if(!e)return;const t={};return(0,c.isDefined)(e.marketing)&&(t.adUserData=e.marketing?"CONSENT_GRANTED":"CONSENT_DENIED"),(0,c.isDefined)(e.personalization)&&(t.adPersonalization=e.personalization?"CONSENT_GRANTED":"CONSENT_DENIED"),Object.keys(t).length>0?t:void 0}(e.consent);t&&(n.consent=t)}else n.consent=o;return n}var p=async function(e,{config:t,mapping:n,data:r,collector:i,env:a}){const{accessToken:s,destinations:c,eventSource:g,validateOnly:d=!1,url:l="https://datamanager.googleapis.com/v1",consent:u,testEventCode:p,logLevel:m="none",userData:v,userId:b,clientId:y,sessionAttributes:h,consentAdUserData:S,consentAdPersonalization:N}=t.settings,w=function(e="none"){const t={debug:0,info:1,warn:2,error:3,none:4}[e];return{debug:(e,n)=>{t<=0&&console.log(`[DataManager] ${e}`,n||"")},info:(e,n)=>{t<=1&&console.log(`[DataManager] ${e}`,n||"")},warn:(e,n)=>{t<=2&&console.warn(`[DataManager] ${e}`,n||"")},error:(e,n)=>{t<=3&&console.error(`[DataManager] ${e}`,n||"")}}}(m),D=v?await(0,o.getMappingValue)(e,{map:v}):{},E=b?await(0,o.getMappingValue)(e,b):void 0,I=y?await(0,o.getMappingValue)(e,y):void 0,O=h?await(0,o.getMappingValue)(e,h):void 0,C="boolean"==typeof S?S:"string"==typeof S&&e.consent?e.consent[S]:void 0,k="boolean"==typeof N?N:"string"==typeof N&&e.consent?e.consent[N]:void 0,T={};(0,o.isObject)(D)&&Object.assign(T,D),void 0!==E&&(T.userId=E),void 0!==I&&(T.clientId=I),void 0!==O&&(T.sessionAttributes=O),void 0!==C&&(T.adUserData=C),void 0!==k&&(T.adPersonalization=k);const A=t.data?await(0,o.getMappingValue)(e,t.data):{},j=(0,o.isObject)(r)?r:{},P={...T,...(0,o.isObject)(A)?A:{},...j},M=await f(e,P);w.debug("Processing event",{name:e.name,id:e.id,timestamp:e.timestamp}),!M.eventSource&&g&&(M.eventSource=g),!M.consent&&u&&(M.consent=u);const $={events:[M],destinations:c};u&&($.consent=u),d&&($.validateOnly=!0),p&&($.testEventCode=p);const q=(null==a?void 0:a.fetch)||fetch,V=`${l}/events:ingest`;w.debug("Sending to Data Manager API",{endpoint:V,eventCount:$.events.length,destinations:c.length,validateOnly:d});const _=await q(V,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify($)});if(!_.ok){const e=await _.text();throw w.error("API request failed",{status:_.status,error:e}),new Error(`Data Manager API error (${_.status}): ${e}`)}const z=await _.json();if(w.debug("API response",{status:_.status,requestId:z.requestId}),z.validationErrors&&z.validationErrors.length>0)throw w.error("Validation errors",{errors:z.validationErrors}),new Error(`Validation errors: ${JSON.stringify(z.validationErrors)}`);w.info("Event processed successfully",{requestId:z.requestId})},m={},v={type:"datamanager",config:{},async init({config:e}){const t=function(e={}){const t=e.settings||{},{accessToken:n,destinations:r}=t;n||(0,s.throwError)("Config settings accessToken missing"),r&&0!==r.length||(0,s.throwError)("Config settings destinations missing or empty");const i={...t,accessToken:n,destinations:r};return{...e,settings:i}}(e);return t},push:async(e,{config:t,mapping:n,data:r,collector:i,env:a})=>await p(e,{config:t,mapping:n,data:r,collector:i,env:a})},b=v;//# 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/format.ts","../src/hash.ts","../src/utils.ts","../src/types/index.ts","../src/examples/index.ts","../src/examples/mapping.ts","../src/examples/basic.ts","../src/schemas.ts","../src/schemas/primitives.ts","../src/schemas/settings.ts","../src/schemas/mapping.ts","../src/schemas/index.ts"],"sourcesContent":["import type { DestinationInterface } from './types';\nimport { getConfig } from './config';\nimport { push } from './push';\n\n// Types\nexport * as DestinationDataManager from './types';\n\n// Examples\nexport * as examples from './examples';\n\n// Schemas\nexport * as schemas from './schemas';\n\nexport const destinationDataManager: DestinationInterface = {\n type: 'datamanager',\n\n config: {},\n\n async init({ config: partialConfig }) {\n const config = getConfig(partialConfig);\n return config;\n },\n\n async push(event, { config, mapping, data, collector, env }) {\n return await push(event, { config, mapping, data, collector, env });\n },\n};\n\nexport default destinationDataManager;\n","import type { Config, Settings, PartialConfig } from './types';\nimport { throwError } from '@walkeros/core';\n\nexport function getConfig(partialConfig: PartialConfig = {}): Config {\n const settings = (partialConfig.settings || {}) as Partial<Settings>;\n const { accessToken, destinations } = settings;\n\n if (!accessToken) throwError('Config settings accessToken missing');\n if (!destinations || destinations.length === 0)\n throwError('Config settings destinations missing or empty');\n\n const settingsConfig: Settings = {\n ...settings,\n accessToken,\n destinations,\n };\n\n return { ...partialConfig, settings: settingsConfig };\n}\n","import type { PushFn } from './types';\nimport type { IngestEventsRequest, IngestEventsResponse } from './types';\nimport { getMappingValue, isObject } from '@walkeros/core';\nimport { formatEvent, formatConsent } from './format';\nimport { createLogger } from './utils';\n\nexport const push: PushFn = async function (\n event,\n { config, mapping, data, collector, env },\n) {\n const {\n accessToken,\n destinations,\n eventSource,\n validateOnly = false,\n url = 'https://datamanager.googleapis.com/v1',\n consent: requestConsent,\n testEventCode,\n logLevel = 'none',\n userData,\n userId,\n clientId,\n sessionAttributes,\n consentAdUserData,\n consentAdPersonalization,\n } = config.settings!;\n\n const logger = createLogger(logLevel);\n\n // Extract Settings guided helpers\n const userDataMapped = userData\n ? await getMappingValue(event, { map: userData })\n : {};\n const userIdMapped = userId\n ? await getMappingValue(event, userId)\n : undefined;\n const clientIdMapped = clientId\n ? await getMappingValue(event, clientId)\n : undefined;\n const sessionAttributesMapped = sessionAttributes\n ? await getMappingValue(event, sessionAttributes)\n : undefined;\n\n // Extract consent from Settings\n const consentAdUserDataValue =\n typeof consentAdUserData === 'boolean'\n ? consentAdUserData\n : typeof consentAdUserData === 'string' && event.consent\n ? event.consent[consentAdUserData]\n : undefined;\n\n const consentAdPersonalizationValue =\n typeof consentAdPersonalization === 'boolean'\n ? consentAdPersonalization\n : typeof consentAdPersonalization === 'string' && event.consent\n ? event.consent[consentAdPersonalization]\n : undefined;\n\n // Build Settings helpers object\n const settingsHelpers: Record<string, unknown> = {};\n if (isObject(userDataMapped)) {\n Object.assign(settingsHelpers, userDataMapped);\n }\n if (userIdMapped !== undefined) settingsHelpers.userId = userIdMapped;\n if (clientIdMapped !== undefined) settingsHelpers.clientId = clientIdMapped;\n if (sessionAttributesMapped !== undefined)\n settingsHelpers.sessionAttributes = sessionAttributesMapped;\n if (consentAdUserDataValue !== undefined)\n settingsHelpers.adUserData = consentAdUserDataValue;\n if (consentAdPersonalizationValue !== undefined)\n settingsHelpers.adPersonalization = consentAdPersonalizationValue;\n\n // Get mapped data from destination config and event mapping\n const configData = config.data\n ? await getMappingValue(event, config.data)\n : {};\n const eventData = isObject(data) ? data : {};\n\n // Merge: Settings helpers < config.data < event mapping data\n const finalData = {\n ...settingsHelpers,\n ...(isObject(configData) ? configData : {}),\n ...eventData,\n };\n\n // Format event for Data Manager API\n const dataManagerEvent = await formatEvent(event, finalData);\n\n logger.debug('Processing event', {\n name: event.name,\n id: event.id,\n timestamp: event.timestamp,\n });\n\n // Apply event source from settings if not set\n if (!dataManagerEvent.eventSource && eventSource) {\n dataManagerEvent.eventSource = eventSource;\n }\n\n // Apply request-level consent if event doesn't have consent\n if (!dataManagerEvent.consent && requestConsent) {\n dataManagerEvent.consent = requestConsent;\n }\n\n // Build API request\n const requestBody: IngestEventsRequest = {\n events: [dataManagerEvent],\n destinations,\n };\n\n // Add optional parameters\n if (requestConsent) {\n requestBody.consent = requestConsent;\n }\n\n if (validateOnly) {\n requestBody.validateOnly = true;\n }\n\n if (testEventCode) {\n requestBody.testEventCode = testEventCode;\n }\n\n // Send to Data Manager API\n const fetchFn = env?.fetch || fetch;\n const endpoint = `${url}/events:ingest`;\n\n logger.debug('Sending to Data Manager API', {\n endpoint,\n eventCount: requestBody.events.length,\n destinations: destinations.length,\n validateOnly,\n });\n\n const response = await fetchFn(endpoint, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n logger.error('API request failed', {\n status: response.status,\n error: errorText,\n });\n throw new Error(\n `Data Manager API error (${response.status}): ${errorText}`,\n );\n }\n\n const result: IngestEventsResponse = await response.json();\n\n logger.debug('API response', {\n status: response.status,\n requestId: result.requestId,\n });\n\n // If validation errors exist, throw them\n if (result.validationErrors && result.validationErrors.length > 0) {\n logger.error('Validation errors', {\n errors: result.validationErrors,\n });\n throw new Error(\n `Validation errors: ${JSON.stringify(result.validationErrors)}`,\n );\n }\n\n logger.info('Event processed successfully', {\n requestId: result.requestId,\n });\n};\n","import type { WalkerOS } from '@walkeros/core';\nimport { isString, isDefined } from '@walkeros/core';\nimport type {\n Event,\n UserData,\n UserIdentifier,\n AdIdentifiers,\n Consent,\n ConsentStatus,\n} from './types';\nimport { hashEmail, hashPhone, hashName } from './hash';\n\n/**\n * Format walkerOS event timestamp to RFC 3339 format\n * https://developers.google.com/data-manager/api/reference/rest/v1/Event\n *\n * walkerOS timestamp is in milliseconds, RFC 3339 format: \"2024-01-15T10:30:00Z\"\n */\nexport function formatTimestamp(timestamp: number): string {\n return new Date(timestamp).toISOString();\n}\n\n/**\n * Format user identifiers from mapped data\n * https://developers.google.com/data-manager/api/reference/rest/v1/UserData\n *\n * User data must be explicitly mapped in the mapping configuration.\n * Max 10 identifiers per event\n */\nexport async function formatUserData(\n data: Record<string, unknown>,\n): Promise<UserData | undefined> {\n const identifiers: UserIdentifier[] = [];\n\n // Extract from mapped data only\n // Email\n if (isString(data.email) && data.email) {\n const hashedEmail = await hashEmail(data.email);\n if (hashedEmail) {\n identifiers.push({ emailAddress: hashedEmail });\n }\n }\n\n // Phone\n if (isString(data.phone) && data.phone) {\n const hashedPhone = await hashPhone(data.phone);\n if (hashedPhone) {\n identifiers.push({ phoneNumber: hashedPhone });\n }\n }\n\n // Address from mapped properties\n const hasAddress =\n data.firstName || data.lastName || data.regionCode || data.postalCode;\n\n if (hasAddress) {\n const address: Record<string, string> = {};\n\n if (isString(data.firstName) && data.firstName) {\n address.givenName = await hashName(data.firstName, 'given');\n }\n\n if (isString(data.lastName) && data.lastName) {\n address.familyName = await hashName(data.lastName, 'family');\n }\n\n // Region code is NOT hashed\n if (isString(data.regionCode) && data.regionCode) {\n address.regionCode = data.regionCode.toUpperCase();\n }\n\n // Postal code is NOT hashed\n if (isString(data.postalCode) && data.postalCode) {\n address.postalCode = data.postalCode;\n }\n\n if (Object.keys(address).length > 0) {\n identifiers.push({ address });\n }\n }\n\n // Limit to 10 identifiers\n if (identifiers.length === 0) return undefined;\n\n return {\n userIdentifiers: identifiers.slice(0, 10),\n };\n}\n\n/**\n * Extract and format attribution identifiers from mapped data\n * https://developers.google.com/data-manager/api/reference/rest/v1/AdIdentifiers\n *\n * Attribution identifiers should be mapped explicitly in the mapping configuration.\n * Example: { gclid: 'context.gclid', gbraid: 'context.gbraid' }\n */\nexport function formatAdIdentifiers(\n data: Record<string, unknown>,\n): AdIdentifiers | undefined {\n const identifiers: AdIdentifiers = {};\n\n // Extract from mapped data (already processed by mapping system)\n if (isString(data.gclid) && data.gclid) {\n identifiers.gclid = data.gclid;\n }\n if (isString(data.gbraid) && data.gbraid) {\n identifiers.gbraid = data.gbraid;\n }\n if (isString(data.wbraid) && data.wbraid) {\n identifiers.wbraid = data.wbraid;\n }\n if (isString(data.sessionAttributes) && data.sessionAttributes) {\n identifiers.sessionAttributes = data.sessionAttributes;\n }\n\n return Object.keys(identifiers).length > 0 ? identifiers : undefined;\n}\n\n/**\n * Map walkerOS consent to Data Manager consent format\n * https://developers.google.com/data-manager/api/devguides/concepts/dma\n *\n * walkerOS: { marketing: true, personalization: false }\n * Data Manager: { adUserData: 'CONSENT_GRANTED', adPersonalization: 'CONSENT_DENIED' }\n */\nexport function formatConsent(\n walkerOSConsent: WalkerOS.Consent | undefined,\n): Consent | undefined {\n if (!walkerOSConsent) return undefined;\n\n const consent: Consent = {};\n\n // Map marketing consent to adUserData\n if (isDefined(walkerOSConsent.marketing)) {\n consent.adUserData = walkerOSConsent.marketing\n ? 'CONSENT_GRANTED'\n : 'CONSENT_DENIED';\n }\n\n // Map personalization consent to adPersonalization\n if (isDefined(walkerOSConsent.personalization)) {\n consent.adPersonalization = walkerOSConsent.personalization\n ? 'CONSENT_GRANTED'\n : 'CONSENT_DENIED';\n }\n\n return Object.keys(consent).length > 0 ? consent : undefined;\n}\n\n/**\n * Format complete event for Data Manager API\n * https://developers.google.com/data-manager/api/reference/rest/v1/Event\n */\nexport async function formatEvent(\n event: WalkerOS.Event,\n mappedData?: Record<string, unknown>,\n): Promise<Event> {\n const dataManagerEvent: Event = {\n eventTimestamp: formatTimestamp(event.timestamp),\n };\n\n // Use only mapped data (no fallback to event.data)\n const data = mappedData || {};\n\n // Transaction ID for deduplication\n if (isString(data.transactionId) && data.transactionId) {\n dataManagerEvent.transactionId = data.transactionId.substring(0, 512);\n }\n\n // Client ID (GA)\n if (isString(data.clientId) && data.clientId) {\n dataManagerEvent.clientId = data.clientId.substring(0, 255);\n }\n\n // User ID\n if (isString(data.userId) && data.userId) {\n dataManagerEvent.userId = data.userId.substring(0, 256);\n }\n\n // User data\n const userData = await formatUserData(data);\n if (userData) {\n dataManagerEvent.userData = userData;\n }\n\n // Attribution identifiers\n const adIdentifiers = formatAdIdentifiers(data);\n if (adIdentifiers) {\n dataManagerEvent.adIdentifiers = adIdentifiers;\n }\n\n // Conversion value\n if (typeof data.conversionValue === 'number') {\n dataManagerEvent.conversionValue = data.conversionValue;\n }\n\n // Currency\n if (isString(data.currency) && data.currency) {\n dataManagerEvent.currency = data.currency.substring(0, 3).toUpperCase();\n }\n\n // Cart data\n if (data.cartData && typeof data.cartData === 'object') {\n dataManagerEvent.cartData = data.cartData as Event['cartData'];\n }\n\n // Event name (for GA4)\n if (isString(data.eventName) && data.eventName) {\n dataManagerEvent.eventName = data.eventName.substring(0, 40);\n }\n\n // Event source\n if (isString(data.eventSource) && data.eventSource) {\n dataManagerEvent.eventSource = data.eventSource as Event['eventSource'];\n }\n\n // Consent - check mapped data first, then fallback to event.consent\n const mappedConsent: Consent = {};\n\n // Check for mapped consent values (from Settings or event mapping)\n if (typeof data.adUserData === 'boolean') {\n mappedConsent.adUserData = data.adUserData\n ? 'CONSENT_GRANTED'\n : 'CONSENT_DENIED';\n }\n if (typeof data.adPersonalization === 'boolean') {\n mappedConsent.adPersonalization = data.adPersonalization\n ? 'CONSENT_GRANTED'\n : 'CONSENT_DENIED';\n }\n\n // If no mapped consent, fall back to event.consent\n if (Object.keys(mappedConsent).length === 0) {\n const eventConsent = formatConsent(event.consent);\n if (eventConsent) {\n dataManagerEvent.consent = eventConsent;\n }\n } else {\n dataManagerEvent.consent = mappedConsent;\n }\n\n return dataManagerEvent;\n}\n","import { isString } from '@walkeros/core';\nimport { getHashServer } from '@walkeros/server-core';\n\n/**\n * Normalize email address according to Google Data Manager requirements\n * https://developers.google.com/data-manager/api/devguides/concepts/formatting#email\n *\n * 1. Trim whitespace\n * 2. Convert to lowercase\n * 3. Remove dots (.) for gmail.com and googlemail.com\n * 4. SHA-256 hash\n */\nexport async function hashEmail(email: string): Promise<string> {\n if (!isString(email) || !email) return '';\n\n // Trim and lowercase\n let normalized = email.trim().toLowerCase();\n\n // Remove dots for Gmail addresses\n if (\n normalized.endsWith('@gmail.com') ||\n normalized.endsWith('@googlemail.com')\n ) {\n const [localPart, domain] = normalized.split('@');\n normalized = `${localPart.replace(/\\./g, '')}@${domain}`;\n }\n\n return getHashServer(normalized);\n}\n\n/**\n * Normalize phone number to E.164 format and hash\n * https://developers.google.com/data-manager/api/devguides/concepts/formatting#phone\n *\n * E.164 format: +[country code][number] (max 15 digits after +)\n * Example: +18005550100\n *\n * 1. Remove all non-digit characters except leading +\n * 2. Ensure it starts with +\n * 3. SHA-256 hash\n */\nexport async function hashPhone(phone: string): Promise<string> {\n if (!isString(phone) || !phone) return '';\n\n // Remove all non-digit characters except + at the start\n let normalized = phone.trim();\n\n // Extract country code if present\n const hasPlus = normalized.startsWith('+');\n\n // Remove all non-digits\n normalized = normalized.replace(/\\D/g, '');\n\n // Add + prefix if it was there or if number is long enough\n if (hasPlus || normalized.length > 10) {\n normalized = `+${normalized}`;\n } else {\n // Assume US number if no country code (default behavior)\n normalized = `+1${normalized}`;\n }\n\n return getHashServer(normalized);\n}\n\n/**\n * Normalize and hash a name (first or last name)\n * https://developers.google.com/data-manager/api/devguides/concepts/formatting#name\n *\n * 1. Trim whitespace\n * 2. Convert to lowercase\n * 3. Remove common prefixes (Mr., Mrs., Dr.) for first names\n * 4. Remove common suffixes (Jr., Sr., III) for last names\n * 5. SHA-256 hash\n */\nexport async function hashName(\n name: string,\n type: 'given' | 'family' = 'given',\n): Promise<string> {\n if (!isString(name) || !name) return '';\n\n // Trim and lowercase\n let normalized = name.trim().toLowerCase();\n\n // Remove prefixes for given names\n if (type === 'given') {\n const prefixes = ['mr.', 'mrs.', 'ms.', 'miss.', 'dr.', 'prof.'];\n for (const prefix of prefixes) {\n if (normalized.startsWith(prefix)) {\n normalized = normalized.substring(prefix.length).trim();\n break;\n }\n }\n }\n\n // Remove suffixes for family names (check with and without space)\n // Sort by length (longest first) to match \"iii\" before \"ii\"\n if (type === 'family') {\n const suffixes = ['jr.', 'sr.', 'iii', 'ii', 'iv', 'v'];\n for (const suffix of suffixes) {\n // Check for suffix with space before it (e.g., \" jr.\")\n const suffixWithSpace = ` ${suffix}`;\n if (normalized.endsWith(suffixWithSpace)) {\n normalized = normalized\n .substring(0, normalized.length - suffixWithSpace.length)\n .trim();\n break;\n }\n // Check for suffix without space\n if (normalized.endsWith(suffix)) {\n normalized = normalized\n .substring(0, normalized.length - suffix.length)\n .trim();\n break;\n }\n }\n }\n\n return getHashServer(normalized);\n}\n\n/**\n * Hash multiple email addresses\n */\nexport async function hashEmails(emails: string[]): Promise<string[]> {\n return Promise.all(emails.map((email) => hashEmail(email)));\n}\n\n/**\n * Hash multiple phone numbers\n */\nexport async function hashPhones(phones: string[]): Promise<string[]> {\n return Promise.all(phones.map((phone) => hashPhone(phone)));\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';\n\n/* eslint-disable no-console */\nexport function createLogger(level: LogLevel = 'none') {\n const levels = { debug: 0, info: 1, warn: 2, error: 3, none: 4 };\n const currentLevel = levels[level];\n\n return {\n debug: (message: string, data?: unknown) => {\n if (currentLevel <= 0)\n console.log(`[DataManager] ${message}`, data || '');\n },\n info: (message: string, data?: unknown) => {\n if (currentLevel <= 1)\n console.log(`[DataManager] ${message}`, data || '');\n },\n warn: (message: string, data?: unknown) => {\n if (currentLevel <= 2)\n console.warn(`[DataManager] ${message}`, data || '');\n },\n error: (message: string, data?: unknown) => {\n if (currentLevel <= 3)\n console.error(`[DataManager] ${message}`, data || '');\n },\n };\n}\n/* eslint-enable no-console */\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationServer } from '@walkeros/server-core';\nimport type { LogLevel } from '../utils';\n\nexport interface Settings {\n /** OAuth 2.0 access token with datamanager scope */\n accessToken: string;\n\n /** Array of destination accounts and conversion actions/user lists */\n destinations: Destination[];\n\n /** Default event source if not specified per event */\n eventSource?: EventSource;\n\n /** Maximum number of events to batch before sending (max 2000) */\n batchSize?: number;\n\n /** Time in milliseconds to wait before auto-flushing batch */\n batchInterval?: number;\n\n /** If true, validate request without ingestion (testing mode) */\n validateOnly?: boolean;\n\n /** Override API endpoint (for testing) */\n url?: string;\n\n /** Request-level consent for all events */\n consent?: Consent;\n\n /** Test event code for debugging (optional) */\n testEventCode?: string;\n\n /** Log level for debugging (optional) */\n logLevel?: LogLevel;\n\n /** Guided helpers: User data mapping (applies to all events) */\n userData?: WalkerOSMapping.Map;\n\n /** Guided helper: First-party user ID */\n userId?: WalkerOSMapping.Value;\n\n /** Guided helper: GA4 client ID */\n clientId?: WalkerOSMapping.Value;\n\n /** Guided helper: Privacy-safe attribution (Google's sessionAttributes) */\n sessionAttributes?: WalkerOSMapping.Value;\n\n /** Consent mapping: Map consent field to adUserData (string = field name, boolean = static value) */\n consentAdUserData?: string | boolean;\n\n /** Consent mapping: Map consent field to adPersonalization (string = field name, boolean = static value) */\n consentAdPersonalization?: string | boolean;\n}\n\nexport interface Mapping {\n // Attribution identifiers (optional, for explicit mapping)\n gclid?: WalkerOSMapping.Value;\n gbraid?: WalkerOSMapping.Value;\n wbraid?: WalkerOSMapping.Value;\n sessionAttributes?: WalkerOSMapping.Value;\n}\n\nexport interface Env extends DestinationServer.Env {\n fetch?: typeof fetch;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env>;\n\nexport interface DestinationInterface\n 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// Google Data Manager API Types\n// https://developers.google.com/data-manager/api/reference\n\n/**\n * Destination account and product identifier\n * https://developers.google.com/data-manager/api/reference/rest/v1/Destination\n */\nexport interface Destination {\n /** Operating account details */\n operatingAccount: OperatingAccount;\n\n /** Product-specific destination ID (conversion action or user list) */\n productDestinationId: string;\n}\n\n/**\n * Operating account information\n */\nexport interface OperatingAccount {\n /** Account ID (e.g., \"123-456-7890\" for Google Ads) */\n accountId: string;\n\n /** Type of account */\n accountType: AccountType;\n}\n\nexport type AccountType =\n | 'GOOGLE_ADS'\n | 'DISPLAY_VIDEO_ADVERTISER'\n | 'DISPLAY_VIDEO_PARTNER'\n | 'GOOGLE_ANALYTICS_PROPERTY';\n\nexport type EventSource = 'WEB' | 'APP' | 'IN_STORE' | 'PHONE' | 'OTHER';\n\n/**\n * Consent for Digital Markets Act (DMA) compliance\n * https://developers.google.com/data-manager/api/devguides/concepts/dma\n */\nexport interface Consent {\n /** Consent for data collection and use */\n adUserData?: ConsentStatus;\n\n /** Consent for ad personalization */\n adPersonalization?: ConsentStatus;\n}\n\nexport type ConsentStatus = 'CONSENT_GRANTED' | 'CONSENT_DENIED';\n\n/**\n * Request body for events.ingest API\n * https://developers.google.com/data-manager/api/reference/rest/v1/events/ingest\n */\nexport interface IngestEventsRequest {\n /** OAuth 2.0 access token */\n access_token?: string;\n\n /** Array of events to ingest (max 2000) */\n events: Event[];\n\n /** Array of destinations for these events (max 10) */\n destinations: Destination[];\n\n /** Request-level consent (overridden by event-level) */\n consent?: Consent;\n\n /** If true, validate without ingestion */\n validateOnly?: boolean;\n\n /** Test event code for debugging */\n testEventCode?: string;\n}\n\n/**\n * Single event for ingestion\n * https://developers.google.com/data-manager/api/reference/rest/v1/Event\n */\nexport interface Event {\n /** Event timestamp in RFC 3339 format */\n eventTimestamp: string;\n\n /** Transaction ID for deduplication (max 512 chars) */\n transactionId?: string;\n\n /** Google Analytics client ID (max 255 chars) */\n clientId?: string;\n\n /** First-party user ID (max 256 chars) */\n userId?: string;\n\n /** User data with identifiers (max 10 identifiers) */\n userData?: UserData;\n\n /** Attribution identifiers */\n adIdentifiers?: AdIdentifiers;\n\n /** Conversion value */\n conversionValue?: number;\n\n /** Currency code (ISO 4217, 3 chars) */\n currency?: string;\n\n /** Shopping cart data */\n cartData?: CartData;\n\n /** Event name for GA4 (max 40 chars, required for GA4) */\n eventName?: string;\n\n /** Source of the event */\n eventSource?: EventSource;\n\n /** Event-level consent (overrides request-level) */\n consent?: Consent;\n}\n\n/**\n * User data with identifiers\n * https://developers.google.com/data-manager/api/reference/rest/v1/UserData\n */\nexport interface UserData {\n /** Array of user identifiers (max 10) */\n userIdentifiers: UserIdentifier[];\n}\n\n/**\n * User identifier (email, phone, or address)\n */\nexport type UserIdentifier =\n | { emailAddress: string }\n | { phoneNumber: string }\n | { address: Address };\n\n/**\n * Address for user identification\n * https://developers.google.com/data-manager/api/reference/rest/v1/Address\n */\nexport interface Address {\n /** Given name (first name) - SHA-256 hashed */\n givenName?: string;\n\n /** Family name (last name) - SHA-256 hashed */\n familyName?: string;\n\n /** ISO-3166-1 alpha-2 country code - NOT hashed (e.g., \"US\", \"GB\") */\n regionCode?: string;\n\n /** Postal code - NOT hashed */\n postalCode?: string;\n}\n\n/**\n * Attribution identifiers\n * https://developers.google.com/data-manager/api/reference/rest/v1/AdIdentifiers\n */\nexport interface AdIdentifiers {\n /** Google Click ID (primary attribution) */\n gclid?: string;\n\n /** iOS attribution identifier (post-ATT) */\n gbraid?: string;\n\n /** Web-to-app attribution identifier */\n wbraid?: string;\n\n /** Session attributes (privacy-safe attribution) */\n sessionAttributes?: string;\n}\n\n/**\n * Shopping cart data\n * https://developers.google.com/data-manager/api/reference/rest/v1/CartData\n */\nexport interface CartData {\n /** Array of cart items (max 200) */\n items: CartItem[];\n}\n\n/**\n * Single cart item\n * https://developers.google.com/data-manager/api/reference/rest/v1/CartItem\n */\nexport interface CartItem {\n /** Merchant product ID (max 127 chars) */\n merchantProductId?: string;\n\n /** Item price */\n price?: number;\n\n /** Item quantity */\n quantity?: number;\n}\n\n/**\n * Response from events.ingest API\n * https://developers.google.com/data-manager/api/reference/rest/v1/IngestEventsResponse\n */\nexport interface IngestEventsResponse {\n /** Unique request ID for status checking */\n requestId: string;\n\n /** Validation errors (only if validateOnly=true) */\n validationErrors?: ValidationError[];\n}\n\n/**\n * Validation error\n */\nexport interface ValidationError {\n /** Error code */\n code: string;\n\n /** Human-readable error message */\n message: string;\n\n /** Field path that caused the error */\n fieldPath?: string;\n}\n\n/**\n * Request status response\n * https://developers.google.com/data-manager/api/reference/rest/v1/requestStatus/retrieve\n */\nexport interface RequestStatusResponse {\n /** Unique request ID */\n requestId: string;\n\n /** Processing state */\n state: RequestState;\n\n /** Number of events successfully ingested */\n eventsIngested?: number;\n\n /** Number of events that failed */\n eventsFailed?: number;\n\n /** Array of errors (if any) */\n errors?: RequestError[];\n}\n\nexport type RequestState =\n | 'STATE_UNSPECIFIED'\n | 'PENDING'\n | 'PROCESSING'\n | 'SUCCEEDED'\n | 'FAILED'\n | 'PARTIALLY_SUCCEEDED';\n\n/**\n * Request error\n */\nexport interface RequestError {\n /** Error code */\n code: string;\n\n /** Human-readable error message */\n message: string;\n\n /** Number of events affected by this error */\n eventCount?: number;\n}\n","export * as mapping from './mapping';\nexport * as basic from './basic';\n","import type { DestinationDataManager } from '..';\nimport { isObject } from '@walkeros/core';\n\n/**\n * Purchase event mapping for Google Ads conversion\n */\nexport const Purchase: DestinationDataManager.Rule = {\n name: 'purchase',\n data: {\n map: {\n // Required fields\n transactionId: 'data.id',\n conversionValue: 'data.total',\n currency: { key: 'data.currency', value: 'USD' },\n eventName: { value: 'purchase' },\n\n // User identification\n userId: 'user.id',\n email: 'user.id', // Will be hashed automatically\n\n // Attribution identifiers (captured by browser source from URL)\n gclid: 'context.gclid', // Google Click ID\n gbraid: 'context.gbraid', // iOS attribution\n wbraid: 'context.wbraid', // Web-to-app\n\n // Shopping cart data\n cartData: {\n map: {\n items: {\n loop: [\n 'nested',\n {\n condition: (entity) =>\n isObject(entity) && entity.entity === 'product',\n map: {\n merchantProductId: 'data.id',\n price: 'data.price',\n quantity: { key: 'data.quantity', value: 1 },\n },\n },\n ],\n },\n },\n },\n },\n },\n};\n\n/**\n * Lead event mapping\n */\nexport const Lead: DestinationDataManager.Rule = {\n name: 'generate_lead',\n data: {\n map: {\n eventName: { value: 'generate_lead' },\n conversionValue: { value: 10 },\n currency: { value: 'USD' },\n },\n },\n};\n\n/**\n * Page view mapping for GA4\n */\nexport const PageView: DestinationDataManager.Rule = {\n name: 'page_view',\n data: {\n map: {\n eventName: { value: 'page_view' },\n },\n },\n};\n\n/**\n * Complete mapping configuration\n */\nexport const mapping = {\n order: {\n complete: Purchase,\n },\n lead: {\n submit: Lead,\n },\n page: {\n view: PageView,\n },\n} satisfies DestinationDataManager.Rules;\n\n/**\n * User data mapping configuration\n * Maps walkerOS user properties to Data Manager user identifiers\n */\nexport const userDataMapping: DestinationDataManager.Config = {\n settings: {\n accessToken: 'ya29.c.xxx',\n destinations: [\n {\n operatingAccount: {\n accountId: '123-456-7890',\n accountType: 'GOOGLE_ADS',\n },\n productDestinationId: 'AW-CONVERSION-123',\n },\n ],\n },\n data: {\n map: {\n email: 'user.id',\n phone: 'data.phone',\n firstName: 'data.firstName',\n lastName: 'data.lastName',\n regionCode: 'data.country',\n postalCode: 'data.zip',\n },\n },\n mapping,\n};\n","import type { DestinationDataManager } from '..';\n\n/**\n * Minimal configuration for Google Data Manager\n */\nexport const minimal: DestinationDataManager.Config = {\n settings: {\n accessToken: 'ya29.c.xxx',\n destinations: [\n {\n operatingAccount: {\n accountId: '123-456-7890',\n accountType: 'GOOGLE_ADS',\n },\n productDestinationId: 'AW-CONVERSION-123',\n },\n ],\n },\n};\n\n/**\n * Complete configuration with all options\n */\nexport const complete: DestinationDataManager.Config = {\n settings: {\n accessToken: 'ya29.c.xxx',\n destinations: [\n {\n operatingAccount: {\n accountId: '123-456-7890',\n accountType: 'GOOGLE_ADS',\n },\n productDestinationId: 'AW-CONVERSION-123',\n },\n {\n operatingAccount: {\n accountId: '987654321',\n accountType: 'GOOGLE_ANALYTICS_PROPERTY',\n },\n productDestinationId: 'G-XXXXXXXXXX',\n },\n ],\n eventSource: 'WEB',\n batchSize: 100,\n batchInterval: 5000,\n validateOnly: false,\n consent: {\n adUserData: 'CONSENT_GRANTED',\n adPersonalization: 'CONSENT_GRANTED',\n },\n\n // Guided helpers (apply to all events)\n userData: {\n email: 'user.id',\n phone: 'data.phone',\n firstName: 'data.firstName',\n lastName: 'data.lastName',\n },\n userId: 'user.id',\n clientId: 'user.device',\n sessionAttributes: 'context.sessionAttributes',\n },\n data: {\n map: {\n eventSource: { value: 'WEB' },\n },\n },\n};\n\n/**\n * GA4-specific configuration\n */\nexport const ga4: DestinationDataManager.Config = {\n settings: {\n accessToken: 'ya29.c.xxx',\n destinations: [\n {\n operatingAccount: {\n accountId: '123456789',\n accountType: 'GOOGLE_ANALYTICS_PROPERTY',\n },\n productDestinationId: 'G-XXXXXXXXXX',\n },\n ],\n eventSource: 'WEB',\n },\n};\n\n/**\n * Debug configuration with logging enabled\n */\nexport const debug: DestinationDataManager.Config = {\n settings: {\n accessToken: 'ya29.c.xxx',\n destinations: [\n {\n operatingAccount: {\n accountId: '123-456-7890',\n accountType: 'GOOGLE_ADS',\n },\n productDestinationId: 'AW-CONVERSION-123',\n },\n ],\n logLevel: 'debug', // Shows all API calls and responses\n },\n};\n","// Browser-safe schema-only exports\n// This file exports ONLY schemas without any Node.js dependencies\nimport { schemas } from './schemas/index';\n\nexport { schemas };\n","import { z } from '@walkeros/core';\n\nexport const AccountTypeSchema = z.enum([\n 'GOOGLE_ADS',\n 'DISPLAY_VIDEO_ADVERTISER',\n 'DISPLAY_VIDEO_PARTNER',\n 'GOOGLE_ANALYTICS_PROPERTY',\n]);\n\nexport const EventSourceSchema = z.enum([\n 'WEB',\n 'APP',\n 'IN_STORE',\n 'PHONE',\n 'OTHER',\n]);\n\nexport const ConsentStatusSchema = z.enum([\n 'CONSENT_GRANTED',\n 'CONSENT_DENIED',\n]);\n\nexport const ConsentSchema = z.object({\n adUserData: ConsentStatusSchema.describe(\n 'Consent for data collection and use',\n ).optional(),\n adPersonalization: ConsentStatusSchema.describe(\n 'Consent for ad personalization',\n ).optional(),\n});\n\nexport const OperatingAccountSchema = z.object({\n accountId: z\n .string()\n .min(1)\n .describe('Account ID (e.g., \"123-456-7890\" for Google Ads)'),\n accountType: AccountTypeSchema.describe('Type of account'),\n});\n\nexport const DestinationSchema = z.object({\n operatingAccount: OperatingAccountSchema.describe(\n 'Operating account details',\n ),\n productDestinationId: z\n .string()\n .min(1)\n .describe(\n 'Product-specific destination ID (conversion action or user list)',\n ),\n});\n","import { z } from '@walkeros/core';\nimport {\n DestinationSchema,\n EventSourceSchema,\n ConsentSchema,\n} from './primitives';\n\nexport const SettingsSchema = z.object({\n accessToken: z\n .string()\n .min(1)\n .describe(\n 'OAuth 2.0 access token with datamanager scope (like ya29.c.xxx)',\n ),\n destinations: z\n .array(DestinationSchema)\n .min(1)\n .max(10)\n .describe(\n 'Array of destination accounts and conversion actions/user lists (max 10)',\n ),\n eventSource: EventSourceSchema.describe(\n 'Default event source if not specified per event (like WEB)',\n ).optional(),\n batchSize: z\n .number()\n .int()\n .min(1)\n .max(2000)\n .describe(\n 'Maximum number of events to batch before sending (max 2000, like 100)',\n )\n .optional(),\n batchInterval: z\n .number()\n .int()\n .min(0)\n .describe(\n 'Time in milliseconds to wait before auto-flushing batch (like 5000)',\n )\n .optional(),\n validateOnly: z\n .boolean()\n .describe('If true, validate request without ingestion (testing mode)')\n .optional(),\n url: z\n .string()\n .url()\n .describe(\n 'Override API endpoint for testing (like https://datamanager.googleapis.com/v1)',\n )\n .optional(),\n consent: ConsentSchema.describe(\n 'Request-level consent for all events',\n ).optional(),\n testEventCode: z\n .string()\n .describe('Test event code for debugging (like TEST12345)')\n .optional(),\n logLevel: z\n .enum(['debug', 'info', 'warn', 'error', 'none'])\n .describe('Log level for debugging (debug shows all API calls)')\n .optional(),\n userData: z\n .record(z.string(), z.unknown())\n .describe(\n \"Guided helper: User data mapping for all events (like { email: 'user.id', phone: 'data.phone' })\",\n )\n .optional(),\n userId: z\n .any()\n .describe(\n \"Guided helper: First-party user ID for all events (like 'user.id')\",\n )\n .optional(),\n clientId: z\n .any()\n .describe(\n \"Guided helper: GA4 client ID for all events (like 'user.device')\",\n )\n .optional(),\n sessionAttributes: z\n .any()\n .describe(\n \"Guided helper: Privacy-safe attribution for all events (like 'context.sessionAttributes')\",\n )\n .optional(),\n consentAdUserData: z\n .union([z.string(), z.boolean()])\n .describe(\n \"Consent mapping: Field name from event.consent (like 'marketing') or static boolean value\",\n )\n .optional(),\n consentAdPersonalization: z\n .union([z.string(), z.boolean()])\n .describe(\n \"Consent mapping: Field name from event.consent (like 'targeting') or static boolean value\",\n )\n .optional(),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core';\n\n// Data Manager uses flexible mapping via walkerOS mapping system\n// No event-specific mapping schema needed (similar to Meta CAPI pattern)\nexport const MappingSchema = z.object({});\n\nexport type Mapping = z.infer<typeof MappingSchema>;\n","export * from './primitives';\nexport * from './settings';\nexport * from './mapping';\n\nimport { zodToSchema } from '@walkeros/core';\nimport { SettingsSchema } from './settings';\nimport { MappingSchema } from './mapping';\n\nimport type { JSONSchema } from '@walkeros/core';\n\nexport const schemas: Record<string, JSONSchema> = {\n settings: zodToSchema(SettingsSchema),\n mapping: zodToSchema(MappingSchema),\n};\n"],"mappings":"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","names":["import_core","import_core","import_core","mapping","import_core","import_core","import_core","import_core","import_core","mapping"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/push.ts","../src/format.ts","../src/hash.ts","../src/utils.ts","../src/types/index.ts"],"sourcesContent":["import type { DestinationInterface } from './types';\nimport { getConfig } from './config';\nimport { push } from './push';\n\n// Types\nexport * as DestinationDataManager from './types';\n\nexport const destinationDataManager: DestinationInterface = {\n type: 'datamanager',\n\n config: {},\n\n async init({ config: partialConfig }) {\n const config = getConfig(partialConfig);\n return config;\n },\n\n async push(event, { config, mapping, data, collector, env }) {\n return await push(event, { config, mapping, data, collector, env });\n },\n};\n\nexport default destinationDataManager;\n","import type { Config, Settings, PartialConfig } from './types';\nimport { throwError } from '@walkeros/core';\n\nexport function getConfig(partialConfig: PartialConfig = {}): Config {\n const settings = (partialConfig.settings || {}) as Partial<Settings>;\n const { accessToken, destinations } = settings;\n\n if (!accessToken) throwError('Config settings accessToken missing');\n if (!destinations || destinations.length === 0)\n throwError('Config settings destinations missing or empty');\n\n const settingsConfig: Settings = {\n ...settings,\n accessToken,\n destinations,\n };\n\n return { ...partialConfig, settings: settingsConfig };\n}\n","import type { PushFn } from './types';\nimport type { IngestEventsRequest, IngestEventsResponse } from './types';\nimport { getMappingValue, isObject } from '@walkeros/core';\nimport { formatEvent, formatConsent } from './format';\nimport { createLogger } from './utils';\n\nexport const push: PushFn = async function (\n event,\n { config, mapping, data, collector, env },\n) {\n const {\n accessToken,\n destinations,\n eventSource,\n validateOnly = false,\n url = 'https://datamanager.googleapis.com/v1',\n consent: requestConsent,\n testEventCode,\n logLevel = 'none',\n userData,\n userId,\n clientId,\n sessionAttributes,\n consentAdUserData,\n consentAdPersonalization,\n } = config.settings!;\n\n const logger = createLogger(logLevel);\n\n // Extract Settings guided helpers\n const userDataMapped = userData\n ? await getMappingValue(event, { map: userData })\n : {};\n const userIdMapped = userId\n ? await getMappingValue(event, userId)\n : undefined;\n const clientIdMapped = clientId\n ? await getMappingValue(event, clientId)\n : undefined;\n const sessionAttributesMapped = sessionAttributes\n ? await getMappingValue(event, sessionAttributes)\n : undefined;\n\n // Extract consent from Settings\n const consentAdUserDataValue =\n typeof consentAdUserData === 'boolean'\n ? consentAdUserData\n : typeof consentAdUserData === 'string' && event.consent\n ? event.consent[consentAdUserData]\n : undefined;\n\n const consentAdPersonalizationValue =\n typeof consentAdPersonalization === 'boolean'\n ? consentAdPersonalization\n : typeof consentAdPersonalization === 'string' && event.consent\n ? event.consent[consentAdPersonalization]\n : undefined;\n\n // Build Settings helpers object\n const settingsHelpers: Record<string, unknown> = {};\n if (isObject(userDataMapped)) {\n Object.assign(settingsHelpers, userDataMapped);\n }\n if (userIdMapped !== undefined) settingsHelpers.userId = userIdMapped;\n if (clientIdMapped !== undefined) settingsHelpers.clientId = clientIdMapped;\n if (sessionAttributesMapped !== undefined)\n settingsHelpers.sessionAttributes = sessionAttributesMapped;\n if (consentAdUserDataValue !== undefined)\n settingsHelpers.adUserData = consentAdUserDataValue;\n if (consentAdPersonalizationValue !== undefined)\n settingsHelpers.adPersonalization = consentAdPersonalizationValue;\n\n // Get mapped data from destination config and event mapping\n const configData = config.data\n ? await getMappingValue(event, config.data)\n : {};\n const eventData = isObject(data) ? data : {};\n\n // Merge: Settings helpers < config.data < event mapping data\n const finalData = {\n ...settingsHelpers,\n ...(isObject(configData) ? configData : {}),\n ...eventData,\n };\n\n // Format event for Data Manager API\n const dataManagerEvent = await formatEvent(event, finalData);\n\n logger.debug('Processing event', {\n name: event.name,\n id: event.id,\n timestamp: event.timestamp,\n });\n\n // Apply event source from settings if not set\n if (!dataManagerEvent.eventSource && eventSource) {\n dataManagerEvent.eventSource = eventSource;\n }\n\n // Apply request-level consent if event doesn't have consent\n if (!dataManagerEvent.consent && requestConsent) {\n dataManagerEvent.consent = requestConsent;\n }\n\n // Build API request\n const requestBody: IngestEventsRequest = {\n events: [dataManagerEvent],\n destinations,\n };\n\n // Add optional parameters\n if (requestConsent) {\n requestBody.consent = requestConsent;\n }\n\n if (validateOnly) {\n requestBody.validateOnly = true;\n }\n\n if (testEventCode) {\n requestBody.testEventCode = testEventCode;\n }\n\n // Send to Data Manager API\n const fetchFn = env?.fetch || fetch;\n const endpoint = `${url}/events:ingest`;\n\n logger.debug('Sending to Data Manager API', {\n endpoint,\n eventCount: requestBody.events.length,\n destinations: destinations.length,\n validateOnly,\n });\n\n const response = await fetchFn(endpoint, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n logger.error('API request failed', {\n status: response.status,\n error: errorText,\n });\n throw new Error(\n `Data Manager API error (${response.status}): ${errorText}`,\n );\n }\n\n const result: IngestEventsResponse = await response.json();\n\n logger.debug('API response', {\n status: response.status,\n requestId: result.requestId,\n });\n\n // If validation errors exist, throw them\n if (result.validationErrors && result.validationErrors.length > 0) {\n logger.error('Validation errors', {\n errors: result.validationErrors,\n });\n throw new Error(\n `Validation errors: ${JSON.stringify(result.validationErrors)}`,\n );\n }\n\n logger.info('Event processed successfully', {\n requestId: result.requestId,\n });\n};\n","import type { WalkerOS } from '@walkeros/core';\nimport { isString, isDefined } from '@walkeros/core';\nimport type {\n Event,\n UserData,\n UserIdentifier,\n AdIdentifiers,\n Consent,\n ConsentStatus,\n} from './types';\nimport { hashEmail, hashPhone, hashName } from './hash';\n\n/**\n * Format walkerOS event timestamp to RFC 3339 format\n * https://developers.google.com/data-manager/api/reference/rest/v1/Event\n *\n * walkerOS timestamp is in milliseconds, RFC 3339 format: \"2024-01-15T10:30:00Z\"\n */\nexport function formatTimestamp(timestamp: number): string {\n return new Date(timestamp).toISOString();\n}\n\n/**\n * Format user identifiers from mapped data\n * https://developers.google.com/data-manager/api/reference/rest/v1/UserData\n *\n * User data must be explicitly mapped in the mapping configuration.\n * Max 10 identifiers per event\n */\nexport async function formatUserData(\n data: Record<string, unknown>,\n): Promise<UserData | undefined> {\n const identifiers: UserIdentifier[] = [];\n\n // Extract from mapped data only\n // Email\n if (isString(data.email) && data.email) {\n const hashedEmail = await hashEmail(data.email);\n if (hashedEmail) {\n identifiers.push({ emailAddress: hashedEmail });\n }\n }\n\n // Phone\n if (isString(data.phone) && data.phone) {\n const hashedPhone = await hashPhone(data.phone);\n if (hashedPhone) {\n identifiers.push({ phoneNumber: hashedPhone });\n }\n }\n\n // Address from mapped properties\n const hasAddress =\n data.firstName || data.lastName || data.regionCode || data.postalCode;\n\n if (hasAddress) {\n const address: Record<string, string> = {};\n\n if (isString(data.firstName) && data.firstName) {\n address.givenName = await hashName(data.firstName, 'given');\n }\n\n if (isString(data.lastName) && data.lastName) {\n address.familyName = await hashName(data.lastName, 'family');\n }\n\n // Region code is NOT hashed\n if (isString(data.regionCode) && data.regionCode) {\n address.regionCode = data.regionCode.toUpperCase();\n }\n\n // Postal code is NOT hashed\n if (isString(data.postalCode) && data.postalCode) {\n address.postalCode = data.postalCode;\n }\n\n if (Object.keys(address).length > 0) {\n identifiers.push({ address });\n }\n }\n\n // Limit to 10 identifiers\n if (identifiers.length === 0) return undefined;\n\n return {\n userIdentifiers: identifiers.slice(0, 10),\n };\n}\n\n/**\n * Extract and format attribution identifiers from mapped data\n * https://developers.google.com/data-manager/api/reference/rest/v1/AdIdentifiers\n *\n * Attribution identifiers should be mapped explicitly in the mapping configuration.\n * Example: { gclid: 'context.gclid', gbraid: 'context.gbraid' }\n */\nexport function formatAdIdentifiers(\n data: Record<string, unknown>,\n): AdIdentifiers | undefined {\n const identifiers: AdIdentifiers = {};\n\n // Extract from mapped data (already processed by mapping system)\n if (isString(data.gclid) && data.gclid) {\n identifiers.gclid = data.gclid;\n }\n if (isString(data.gbraid) && data.gbraid) {\n identifiers.gbraid = data.gbraid;\n }\n if (isString(data.wbraid) && data.wbraid) {\n identifiers.wbraid = data.wbraid;\n }\n if (isString(data.sessionAttributes) && data.sessionAttributes) {\n identifiers.sessionAttributes = data.sessionAttributes;\n }\n\n return Object.keys(identifiers).length > 0 ? identifiers : undefined;\n}\n\n/**\n * Map walkerOS consent to Data Manager consent format\n * https://developers.google.com/data-manager/api/devguides/concepts/dma\n *\n * walkerOS: { marketing: true, personalization: false }\n * Data Manager: { adUserData: 'CONSENT_GRANTED', adPersonalization: 'CONSENT_DENIED' }\n */\nexport function formatConsent(\n walkerOSConsent: WalkerOS.Consent | undefined,\n): Consent | undefined {\n if (!walkerOSConsent) return undefined;\n\n const consent: Consent = {};\n\n // Map marketing consent to adUserData\n if (isDefined(walkerOSConsent.marketing)) {\n consent.adUserData = walkerOSConsent.marketing\n ? 'CONSENT_GRANTED'\n : 'CONSENT_DENIED';\n }\n\n // Map personalization consent to adPersonalization\n if (isDefined(walkerOSConsent.personalization)) {\n consent.adPersonalization = walkerOSConsent.personalization\n ? 'CONSENT_GRANTED'\n : 'CONSENT_DENIED';\n }\n\n return Object.keys(consent).length > 0 ? consent : undefined;\n}\n\n/**\n * Format complete event for Data Manager API\n * https://developers.google.com/data-manager/api/reference/rest/v1/Event\n */\nexport async function formatEvent(\n event: WalkerOS.Event,\n mappedData?: Record<string, unknown>,\n): Promise<Event> {\n const dataManagerEvent: Event = {\n eventTimestamp: formatTimestamp(event.timestamp),\n };\n\n // Use only mapped data (no fallback to event.data)\n const data = mappedData || {};\n\n // Transaction ID for deduplication\n if (isString(data.transactionId) && data.transactionId) {\n dataManagerEvent.transactionId = data.transactionId.substring(0, 512);\n }\n\n // Client ID (GA)\n if (isString(data.clientId) && data.clientId) {\n dataManagerEvent.clientId = data.clientId.substring(0, 255);\n }\n\n // User ID\n if (isString(data.userId) && data.userId) {\n dataManagerEvent.userId = data.userId.substring(0, 256);\n }\n\n // User data\n const userData = await formatUserData(data);\n if (userData) {\n dataManagerEvent.userData = userData;\n }\n\n // Attribution identifiers\n const adIdentifiers = formatAdIdentifiers(data);\n if (adIdentifiers) {\n dataManagerEvent.adIdentifiers = adIdentifiers;\n }\n\n // Conversion value\n if (typeof data.conversionValue === 'number') {\n dataManagerEvent.conversionValue = data.conversionValue;\n }\n\n // Currency\n if (isString(data.currency) && data.currency) {\n dataManagerEvent.currency = data.currency.substring(0, 3).toUpperCase();\n }\n\n // Cart data\n if (data.cartData && typeof data.cartData === 'object') {\n dataManagerEvent.cartData = data.cartData as Event['cartData'];\n }\n\n // Event name (for GA4)\n if (isString(data.eventName) && data.eventName) {\n dataManagerEvent.eventName = data.eventName.substring(0, 40);\n }\n\n // Event source\n if (isString(data.eventSource) && data.eventSource) {\n dataManagerEvent.eventSource = data.eventSource as Event['eventSource'];\n }\n\n // Consent - check mapped data first, then fallback to event.consent\n const mappedConsent: Consent = {};\n\n // Check for mapped consent values (from Settings or event mapping)\n if (typeof data.adUserData === 'boolean') {\n mappedConsent.adUserData = data.adUserData\n ? 'CONSENT_GRANTED'\n : 'CONSENT_DENIED';\n }\n if (typeof data.adPersonalization === 'boolean') {\n mappedConsent.adPersonalization = data.adPersonalization\n ? 'CONSENT_GRANTED'\n : 'CONSENT_DENIED';\n }\n\n // If no mapped consent, fall back to event.consent\n if (Object.keys(mappedConsent).length === 0) {\n const eventConsent = formatConsent(event.consent);\n if (eventConsent) {\n dataManagerEvent.consent = eventConsent;\n }\n } else {\n dataManagerEvent.consent = mappedConsent;\n }\n\n return dataManagerEvent;\n}\n","import { isString } from '@walkeros/core';\nimport { getHashServer } from '@walkeros/server-core';\n\n/**\n * Normalize email address according to Google Data Manager requirements\n * https://developers.google.com/data-manager/api/devguides/concepts/formatting#email\n *\n * 1. Trim whitespace\n * 2. Convert to lowercase\n * 3. Remove dots (.) for gmail.com and googlemail.com\n * 4. SHA-256 hash\n */\nexport async function hashEmail(email: string): Promise<string> {\n if (!isString(email) || !email) return '';\n\n // Trim and lowercase\n let normalized = email.trim().toLowerCase();\n\n // Remove dots for Gmail addresses\n if (\n normalized.endsWith('@gmail.com') ||\n normalized.endsWith('@googlemail.com')\n ) {\n const [localPart, domain] = normalized.split('@');\n normalized = `${localPart.replace(/\\./g, '')}@${domain}`;\n }\n\n return getHashServer(normalized);\n}\n\n/**\n * Normalize phone number to E.164 format and hash\n * https://developers.google.com/data-manager/api/devguides/concepts/formatting#phone\n *\n * E.164 format: +[country code][number] (max 15 digits after +)\n * Example: +18005550100\n *\n * 1. Remove all non-digit characters except leading +\n * 2. Ensure it starts with +\n * 3. SHA-256 hash\n */\nexport async function hashPhone(phone: string): Promise<string> {\n if (!isString(phone) || !phone) return '';\n\n // Remove all non-digit characters except + at the start\n let normalized = phone.trim();\n\n // Extract country code if present\n const hasPlus = normalized.startsWith('+');\n\n // Remove all non-digits\n normalized = normalized.replace(/\\D/g, '');\n\n // Add + prefix if it was there or if number is long enough\n if (hasPlus || normalized.length > 10) {\n normalized = `+${normalized}`;\n } else {\n // Assume US number if no country code (default behavior)\n normalized = `+1${normalized}`;\n }\n\n return getHashServer(normalized);\n}\n\n/**\n * Normalize and hash a name (first or last name)\n * https://developers.google.com/data-manager/api/devguides/concepts/formatting#name\n *\n * 1. Trim whitespace\n * 2. Convert to lowercase\n * 3. Remove common prefixes (Mr., Mrs., Dr.) for first names\n * 4. Remove common suffixes (Jr., Sr., III) for last names\n * 5. SHA-256 hash\n */\nexport async function hashName(\n name: string,\n type: 'given' | 'family' = 'given',\n): Promise<string> {\n if (!isString(name) || !name) return '';\n\n // Trim and lowercase\n let normalized = name.trim().toLowerCase();\n\n // Remove prefixes for given names\n if (type === 'given') {\n const prefixes = ['mr.', 'mrs.', 'ms.', 'miss.', 'dr.', 'prof.'];\n for (const prefix of prefixes) {\n if (normalized.startsWith(prefix)) {\n normalized = normalized.substring(prefix.length).trim();\n break;\n }\n }\n }\n\n // Remove suffixes for family names (check with and without space)\n // Sort by length (longest first) to match \"iii\" before \"ii\"\n if (type === 'family') {\n const suffixes = ['jr.', 'sr.', 'iii', 'ii', 'iv', 'v'];\n for (const suffix of suffixes) {\n // Check for suffix with space before it (e.g., \" jr.\")\n const suffixWithSpace = ` ${suffix}`;\n if (normalized.endsWith(suffixWithSpace)) {\n normalized = normalized\n .substring(0, normalized.length - suffixWithSpace.length)\n .trim();\n break;\n }\n // Check for suffix without space\n if (normalized.endsWith(suffix)) {\n normalized = normalized\n .substring(0, normalized.length - suffix.length)\n .trim();\n break;\n }\n }\n }\n\n return getHashServer(normalized);\n}\n\n/**\n * Hash multiple email addresses\n */\nexport async function hashEmails(emails: string[]): Promise<string[]> {\n return Promise.all(emails.map((email) => hashEmail(email)));\n}\n\n/**\n * Hash multiple phone numbers\n */\nexport async function hashPhones(phones: string[]): Promise<string[]> {\n return Promise.all(phones.map((phone) => hashPhone(phone)));\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';\n\n/* eslint-disable no-console */\nexport function createLogger(level: LogLevel = 'none') {\n const levels = { debug: 0, info: 1, warn: 2, error: 3, none: 4 };\n const currentLevel = levels[level];\n\n return {\n debug: (message: string, data?: unknown) => {\n if (currentLevel <= 0)\n console.log(`[DataManager] ${message}`, data || '');\n },\n info: (message: string, data?: unknown) => {\n if (currentLevel <= 1)\n console.log(`[DataManager] ${message}`, data || '');\n },\n warn: (message: string, data?: unknown) => {\n if (currentLevel <= 2)\n console.warn(`[DataManager] ${message}`, data || '');\n },\n error: (message: string, data?: unknown) => {\n if (currentLevel <= 3)\n console.error(`[DataManager] ${message}`, data || '');\n },\n };\n}\n/* eslint-enable no-console */\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationServer } from '@walkeros/server-core';\nimport type { LogLevel } from '../utils';\n\nexport interface Settings {\n /** OAuth 2.0 access token with datamanager scope */\n accessToken: string;\n\n /** Array of destination accounts and conversion actions/user lists */\n destinations: Destination[];\n\n /** Default event source if not specified per event */\n eventSource?: EventSource;\n\n /** Maximum number of events to batch before sending (max 2000) */\n batchSize?: number;\n\n /** Time in milliseconds to wait before auto-flushing batch */\n batchInterval?: number;\n\n /** If true, validate request without ingestion (testing mode) */\n validateOnly?: boolean;\n\n /** Override API endpoint (for testing) */\n url?: string;\n\n /** Request-level consent for all events */\n consent?: Consent;\n\n /** Test event code for debugging (optional) */\n testEventCode?: string;\n\n /** Log level for debugging (optional) */\n logLevel?: LogLevel;\n\n /** Guided helpers: User data mapping (applies to all events) */\n userData?: WalkerOSMapping.Map;\n\n /** Guided helper: First-party user ID */\n userId?: WalkerOSMapping.Value;\n\n /** Guided helper: GA4 client ID */\n clientId?: WalkerOSMapping.Value;\n\n /** Guided helper: Privacy-safe attribution (Google's sessionAttributes) */\n sessionAttributes?: WalkerOSMapping.Value;\n\n /** Consent mapping: Map consent field to adUserData (string = field name, boolean = static value) */\n consentAdUserData?: string | boolean;\n\n /** Consent mapping: Map consent field to adPersonalization (string = field name, boolean = static value) */\n consentAdPersonalization?: string | boolean;\n}\n\nexport interface Mapping {\n // Attribution identifiers (optional, for explicit mapping)\n gclid?: WalkerOSMapping.Value;\n gbraid?: WalkerOSMapping.Value;\n wbraid?: WalkerOSMapping.Value;\n sessionAttributes?: WalkerOSMapping.Value;\n}\n\nexport interface Env extends DestinationServer.Env {\n fetch?: typeof fetch;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env>;\n\nexport interface DestinationInterface\n 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// Google Data Manager API Types\n// https://developers.google.com/data-manager/api/reference\n\n/**\n * Destination account and product identifier\n * https://developers.google.com/data-manager/api/reference/rest/v1/Destination\n */\nexport interface Destination {\n /** Operating account details */\n operatingAccount: OperatingAccount;\n\n /** Product-specific destination ID (conversion action or user list) */\n productDestinationId: string;\n}\n\n/**\n * Operating account information\n */\nexport interface OperatingAccount {\n /** Account ID (e.g., \"123-456-7890\" for Google Ads) */\n accountId: string;\n\n /** Type of account */\n accountType: AccountType;\n}\n\nexport type AccountType =\n | 'GOOGLE_ADS'\n | 'DISPLAY_VIDEO_ADVERTISER'\n | 'DISPLAY_VIDEO_PARTNER'\n | 'GOOGLE_ANALYTICS_PROPERTY';\n\nexport type EventSource = 'WEB' | 'APP' | 'IN_STORE' | 'PHONE' | 'OTHER';\n\n/**\n * Consent for Digital Markets Act (DMA) compliance\n * https://developers.google.com/data-manager/api/devguides/concepts/dma\n */\nexport interface Consent {\n /** Consent for data collection and use */\n adUserData?: ConsentStatus;\n\n /** Consent for ad personalization */\n adPersonalization?: ConsentStatus;\n}\n\nexport type ConsentStatus = 'CONSENT_GRANTED' | 'CONSENT_DENIED';\n\n/**\n * Request body for events.ingest API\n * https://developers.google.com/data-manager/api/reference/rest/v1/events/ingest\n */\nexport interface IngestEventsRequest {\n /** OAuth 2.0 access token */\n access_token?: string;\n\n /** Array of events to ingest (max 2000) */\n events: Event[];\n\n /** Array of destinations for these events (max 10) */\n destinations: Destination[];\n\n /** Request-level consent (overridden by event-level) */\n consent?: Consent;\n\n /** If true, validate without ingestion */\n validateOnly?: boolean;\n\n /** Test event code for debugging */\n testEventCode?: string;\n}\n\n/**\n * Single event for ingestion\n * https://developers.google.com/data-manager/api/reference/rest/v1/Event\n */\nexport interface Event {\n /** Event timestamp in RFC 3339 format */\n eventTimestamp: string;\n\n /** Transaction ID for deduplication (max 512 chars) */\n transactionId?: string;\n\n /** Google Analytics client ID (max 255 chars) */\n clientId?: string;\n\n /** First-party user ID (max 256 chars) */\n userId?: string;\n\n /** User data with identifiers (max 10 identifiers) */\n userData?: UserData;\n\n /** Attribution identifiers */\n adIdentifiers?: AdIdentifiers;\n\n /** Conversion value */\n conversionValue?: number;\n\n /** Currency code (ISO 4217, 3 chars) */\n currency?: string;\n\n /** Shopping cart data */\n cartData?: CartData;\n\n /** Event name for GA4 (max 40 chars, required for GA4) */\n eventName?: string;\n\n /** Source of the event */\n eventSource?: EventSource;\n\n /** Event-level consent (overrides request-level) */\n consent?: Consent;\n}\n\n/**\n * User data with identifiers\n * https://developers.google.com/data-manager/api/reference/rest/v1/UserData\n */\nexport interface UserData {\n /** Array of user identifiers (max 10) */\n userIdentifiers: UserIdentifier[];\n}\n\n/**\n * User identifier (email, phone, or address)\n */\nexport type UserIdentifier =\n | { emailAddress: string }\n | { phoneNumber: string }\n | { address: Address };\n\n/**\n * Address for user identification\n * https://developers.google.com/data-manager/api/reference/rest/v1/Address\n */\nexport interface Address {\n /** Given name (first name) - SHA-256 hashed */\n givenName?: string;\n\n /** Family name (last name) - SHA-256 hashed */\n familyName?: string;\n\n /** ISO-3166-1 alpha-2 country code - NOT hashed (e.g., \"US\", \"GB\") */\n regionCode?: string;\n\n /** Postal code - NOT hashed */\n postalCode?: string;\n}\n\n/**\n * Attribution identifiers\n * https://developers.google.com/data-manager/api/reference/rest/v1/AdIdentifiers\n */\nexport interface AdIdentifiers {\n /** Google Click ID (primary attribution) */\n gclid?: string;\n\n /** iOS attribution identifier (post-ATT) */\n gbraid?: string;\n\n /** Web-to-app attribution identifier */\n wbraid?: string;\n\n /** Session attributes (privacy-safe attribution) */\n sessionAttributes?: string;\n}\n\n/**\n * Shopping cart data\n * https://developers.google.com/data-manager/api/reference/rest/v1/CartData\n */\nexport interface CartData {\n /** Array of cart items (max 200) */\n items: CartItem[];\n}\n\n/**\n * Single cart item\n * https://developers.google.com/data-manager/api/reference/rest/v1/CartItem\n */\nexport interface CartItem {\n /** Merchant product ID (max 127 chars) */\n merchantProductId?: string;\n\n /** Item price */\n price?: number;\n\n /** Item quantity */\n quantity?: number;\n}\n\n/**\n * Response from events.ingest API\n * https://developers.google.com/data-manager/api/reference/rest/v1/IngestEventsResponse\n */\nexport interface IngestEventsResponse {\n /** Unique request ID for status checking */\n requestId: string;\n\n /** Validation errors (only if validateOnly=true) */\n validationErrors?: ValidationError[];\n}\n\n/**\n * Validation error\n */\nexport interface ValidationError {\n /** Error code */\n code: string;\n\n /** Human-readable error message */\n message: string;\n\n /** Field path that caused the error */\n fieldPath?: string;\n}\n\n/**\n * Request status response\n * https://developers.google.com/data-manager/api/reference/rest/v1/requestStatus/retrieve\n */\nexport interface RequestStatusResponse {\n /** Unique request ID */\n requestId: string;\n\n /** Processing state */\n state: RequestState;\n\n /** Number of events successfully ingested */\n eventsIngested?: number;\n\n /** Number of events that failed */\n eventsFailed?: number;\n\n /** Array of errors (if any) */\n errors?: RequestError[];\n}\n\nexport type RequestState =\n | 'STATE_UNSPECIFIED'\n | 'PENDING'\n | 'PROCESSING'\n | 'SUCCEEDED'\n | 'FAILED'\n | 'PARTIALLY_SUCCEEDED';\n\n/**\n * Request error\n */\nexport interface RequestError {\n /** Error code */\n code: string;\n\n /** Human-readable error message */\n message: string;\n\n /** Number of events affected by this error */\n eventCount?: number;\n}\n"],"mappings":"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","names":["import_core","import_core","import_core"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e=Object.defineProperty,t=(t,n)=>{for(var a in n)e(t,a,{get:n[a],enumerable:!0})};import{throwError as n}from"@walkeros/core";import{getMappingValue as a,isObject as o}from"@walkeros/core";import{isString as i,isDefined as s}from"@walkeros/core";import{isString as r}from"@walkeros/core";import{getHashServer as c}from"@walkeros/server-core";async function d(e,t="given"){if(!r(e)||!e)return"";let n=e.trim().toLowerCase();if("given"===t){const e=["mr.","mrs.","ms.","miss.","dr.","prof."];for(const t of e)if(n.startsWith(t)){n=n.substring(t.length).trim();break}}if("family"===t){const e=["jr.","sr.","iii","ii","iv","v"];for(const t of e){const e=` ${t}`;if(n.endsWith(e)){n=n.substring(0,n.length-e.length).trim();break}if(n.endsWith(t)){n=n.substring(0,n.length-t.length).trim();break}}}return c(n)}async function l(e){const t=[];if(i(e.email)&&e.email){const n=await async function(e){if(!r(e)||!e)return"";let t=e.trim().toLowerCase();if(t.endsWith("@gmail.com")||t.endsWith("@googlemail.com")){const[e,n]=t.split("@");t=`${e.replace(/\./g,"")}@${n}`}return c(t)}(e.email);n&&t.push({emailAddress:n})}if(i(e.phone)&&e.phone){const n=await async function(e){if(!r(e)||!e)return"";let t=e.trim();const n=t.startsWith("+");return t=t.replace(/\D/g,""),t=n||t.length>10?`+${t}`:`+1${t}`,c(t)}(e.phone);n&&t.push({phoneNumber:n})}if(e.firstName||e.lastName||e.regionCode||e.postalCode){const n={};i(e.firstName)&&e.firstName&&(n.givenName=await d(e.firstName,"given")),i(e.lastName)&&e.lastName&&(n.familyName=await d(e.lastName,"family")),i(e.regionCode)&&e.regionCode&&(n.regionCode=e.regionCode.toUpperCase()),i(e.postalCode)&&e.postalCode&&(n.postalCode=e.postalCode),Object.keys(n).length>0&&t.push({address:n})}if(0!==t.length)return{userIdentifiers:t.slice(0,10)}}async function u(e,t){const n={eventTimestamp:(a=e.timestamp,new Date(a).toISOString())};var a;const o=t||{};i(o.transactionId)&&o.transactionId&&(n.transactionId=o.transactionId.substring(0,512)),i(o.clientId)&&o.clientId&&(n.clientId=o.clientId.substring(0,255)),i(o.userId)&&o.userId&&(n.userId=o.userId.substring(0,256));const r=await l(o);r&&(n.userData=r);const c=function(e){const t={};return i(e.gclid)&&e.gclid&&(t.gclid=e.gclid),i(e.gbraid)&&e.gbraid&&(t.gbraid=e.gbraid),i(e.wbraid)&&e.wbraid&&(t.wbraid=e.wbraid),i(e.sessionAttributes)&&e.sessionAttributes&&(t.sessionAttributes=e.sessionAttributes),Object.keys(t).length>0?t:void 0}(o);c&&(n.adIdentifiers=c),"number"==typeof o.conversionValue&&(n.conversionValue=o.conversionValue),i(o.currency)&&o.currency&&(n.currency=o.currency.substring(0,3).toUpperCase()),o.cartData&&"object"==typeof o.cartData&&(n.cartData=o.cartData),i(o.eventName)&&o.eventName&&(n.eventName=o.eventName.substring(0,40)),i(o.eventSource)&&o.eventSource&&(n.eventSource=o.eventSource);const d={};if("boolean"==typeof o.adUserData&&(d.adUserData=o.adUserData?"CONSENT_GRANTED":"CONSENT_DENIED"),"boolean"==typeof o.adPersonalization&&(d.adPersonalization=o.adPersonalization?"CONSENT_GRANTED":"CONSENT_DENIED"),0===Object.keys(d).length){const t=function(e){if(!e)return;const t={};return s(e.marketing)&&(t.adUserData=e.marketing?"CONSENT_GRANTED":"CONSENT_DENIED"),s(e.personalization)&&(t.adPersonalization=e.personalization?"CONSENT_GRANTED":"CONSENT_DENIED"),Object.keys(t).length>0?t:void 0}(e.consent);t&&(n.consent=t)}else n.consent=d;return n}var g=async function(e,{config:t,mapping:n,data:i,collector:s,env:r}){const{accessToken:c,destinations:d,eventSource:l,validateOnly:g=!1,url:p="https://datamanager.googleapis.com/v1",consent:m,testEventCode:v,logLevel:f="none",userData:b,userId:N,clientId:I,sessionAttributes:y,consentAdUserData:D,consentAdPersonalization:E}=t.settings,h=function(e="none"){const t={debug:0,info:1,warn:2,error:3,none:4}[e];return{debug:(e,n)=>{t<=0&&console.log(`[DataManager] ${e}`,n||"")},info:(e,n)=>{t<=1&&console.log(`[DataManager] ${e}`,n||"")},warn:(e,n)=>{t<=2&&console.warn(`[DataManager] ${e}`,n||"")},error:(e,n)=>{t<=3&&console.error(`[DataManager] ${e}`,n||"")}}}(f),O=b?await a(e,{map:b}):{},A=N?await a(e,N):void 0,T=I?await a(e,I):void 0,w=y?await a(e,y):void 0,S="boolean"==typeof D?D:"string"==typeof D&&e.consent?e.consent[D]:void 0,k="boolean"==typeof E?E:"string"==typeof E&&e.consent?e.consent[E]:void 0,C={};o(O)&&Object.assign(C,O),void 0!==A&&(C.userId=A),void 0!==T&&(C.clientId=T),void 0!==w&&(C.sessionAttributes=w),void 0!==S&&(C.adUserData=S),void 0!==k&&(C.adPersonalization=k);const P=t.data?await a(e,t.data):{},_=o(i)?i:{},x={...C,...o(P)?P:{},..._},G=await u(e,x);h.debug("Processing event",{name:e.name,id:e.id,timestamp:e.timestamp}),!G.eventSource&&l&&(G.eventSource=l),!G.consent&&m&&(G.consent=m);const R={events:[G],destinations:d};m&&(R.consent=m),g&&(R.validateOnly=!0),v&&(R.testEventCode=v);const L=(null==r?void 0:r.fetch)||fetch,X=`${p}/events:ingest`;h.debug("Sending to Data Manager API",{endpoint:X,eventCount:R.events.length,destinations:d.length,validateOnly:g});const z=await L(X,{method:"POST",headers:{Authorization:`Bearer ${c}`,"Content-Type":"application/json"},body:JSON.stringify(R)});if(!z.ok){const e=await z.text();throw h.error("API request failed",{status:z.status,error:e}),new Error(`Data Manager API error (${z.status}): ${e}`)}const j=await z.json();if(h.debug("API response",{status:z.status,requestId:j.requestId}),j.validationErrors&&j.validationErrors.length>0)throw h.error("Validation errors",{errors:j.validationErrors}),new Error(`Validation errors: ${JSON.stringify(j.validationErrors)}`);h.info("Event processed successfully",{requestId:j.requestId})},p={},m={};t(m,{basic:()=>E,mapping:()=>v});var v={};t(v,{Lead:()=>N,PageView:()=>I,Purchase:()=>b,mapping:()=>y,userDataMapping:()=>D});import{isObject as f}from"@walkeros/core";var b={name:"purchase",data:{map:{transactionId:"data.id",conversionValue:"data.total",currency:{key:"data.currency",value:"USD"},eventName:{value:"purchase"},userId:"user.id",email:"user.id",gclid:"context.gclid",gbraid:"context.gbraid",wbraid:"context.wbraid",cartData:{map:{items:{loop:["nested",{condition:e=>f(e)&&"product"===e.entity,map:{merchantProductId:"data.id",price:"data.price",quantity:{key:"data.quantity",value:1}}}]}}}}}},N={name:"generate_lead",data:{map:{eventName:{value:"generate_lead"},conversionValue:{value:10},currency:{value:"USD"}}}},I={name:"page_view",data:{map:{eventName:{value:"page_view"}}}},y={order:{complete:b},lead:{submit:N},page:{view:I}},D={settings:{accessToken:"ya29.c.xxx",destinations:[{operatingAccount:{accountId:"123-456-7890",accountType:"GOOGLE_ADS"},productDestinationId:"AW-CONVERSION-123"}]},data:{map:{email:"user.id",phone:"data.phone",firstName:"data.firstName",lastName:"data.lastName",regionCode:"data.country",postalCode:"data.zip"}},mapping:y},E={};t(E,{complete:()=>O,debug:()=>T,ga4:()=>A,minimal:()=>h});var h={settings:{accessToken:"ya29.c.xxx",destinations:[{operatingAccount:{accountId:"123-456-7890",accountType:"GOOGLE_ADS"},productDestinationId:"AW-CONVERSION-123"}]}},O={settings:{accessToken:"ya29.c.xxx",destinations:[{operatingAccount:{accountId:"123-456-7890",accountType:"GOOGLE_ADS"},productDestinationId:"AW-CONVERSION-123"},{operatingAccount:{accountId:"987654321",accountType:"GOOGLE_ANALYTICS_PROPERTY"},productDestinationId:"G-XXXXXXXXXX"}],eventSource:"WEB",batchSize:100,batchInterval:5e3,validateOnly:!1,consent:{adUserData:"CONSENT_GRANTED",adPersonalization:"CONSENT_GRANTED"},userData:{email:"user.id",phone:"data.phone",firstName:"data.firstName",lastName:"data.lastName"},userId:"user.id",clientId:"user.device",sessionAttributes:"context.sessionAttributes"},data:{map:{eventSource:{value:"WEB"}}}},A={settings:{accessToken:"ya29.c.xxx",destinations:[{operatingAccount:{accountId:"123456789",accountType:"GOOGLE_ANALYTICS_PROPERTY"},productDestinationId:"G-XXXXXXXXXX"}],eventSource:"WEB"}},T={settings:{accessToken:"ya29.c.xxx",destinations:[{operatingAccount:{accountId:"123-456-7890",accountType:"GOOGLE_ADS"},productDestinationId:"AW-CONVERSION-123"}],logLevel:"debug"}},w={};t(w,{schemas:()=>V});import{z as S}from"@walkeros/core";var k=S.enum(["GOOGLE_ADS","DISPLAY_VIDEO_ADVERTISER","DISPLAY_VIDEO_PARTNER","GOOGLE_ANALYTICS_PROPERTY"]),C=S.enum(["WEB","APP","IN_STORE","PHONE","OTHER"]),P=S.enum(["CONSENT_GRANTED","CONSENT_DENIED"]),_=S.object({adUserData:P.describe("Consent for data collection and use").optional(),adPersonalization:P.describe("Consent for ad personalization").optional()}),x=S.object({accountId:S.string().min(1).describe('Account ID (e.g., "123-456-7890" for Google Ads)'),accountType:k.describe("Type of account")}),G=S.object({operatingAccount:x.describe("Operating account details"),productDestinationId:S.string().min(1).describe("Product-specific destination ID (conversion action or user list)")});import{z as R}from"@walkeros/core";var L=R.object({accessToken:R.string().min(1).describe("OAuth 2.0 access token with datamanager scope (like ya29.c.xxx)"),destinations:R.array(G).min(1).max(10).describe("Array of destination accounts and conversion actions/user lists (max 10)"),eventSource:C.describe("Default event source if not specified per event (like WEB)").optional(),batchSize:R.number().int().min(1).max(2e3).describe("Maximum number of events to batch before sending (max 2000, like 100)").optional(),batchInterval:R.number().int().min(0).describe("Time in milliseconds to wait before auto-flushing batch (like 5000)").optional(),validateOnly:R.boolean().describe("If true, validate request without ingestion (testing mode)").optional(),url:R.string().url().describe("Override API endpoint for testing (like https://datamanager.googleapis.com/v1)").optional(),consent:_.describe("Request-level consent for all events").optional(),testEventCode:R.string().describe("Test event code for debugging (like TEST12345)").optional(),logLevel:R.enum(["debug","info","warn","error","none"]).describe("Log level for debugging (debug shows all API calls)").optional(),userData:R.record(R.string(),R.unknown()).describe("Guided helper: User data mapping for all events (like { email: 'user.id', phone: 'data.phone' })").optional(),userId:R.any().describe("Guided helper: First-party user ID for all events (like 'user.id')").optional(),clientId:R.any().describe("Guided helper: GA4 client ID for all events (like 'user.device')").optional(),sessionAttributes:R.any().describe("Guided helper: Privacy-safe attribution for all events (like 'context.sessionAttributes')").optional(),consentAdUserData:R.union([R.string(),R.boolean()]).describe("Consent mapping: Field name from event.consent (like 'marketing') or static boolean value").optional(),consentAdPersonalization:R.union([R.string(),R.boolean()]).describe("Consent mapping: Field name from event.consent (like 'targeting') or static boolean value").optional()});import{z as X}from"@walkeros/core";var z=X.object({});import{zodToSchema as j}from"@walkeros/core";var V={settings:j(L),mapping:j(z)},W={type:"datamanager",config:{},async init({config:e}){const t=function(e={}){const t=e.settings||{},{accessToken:a,destinations:o}=t;a||n("Config settings accessToken missing"),o&&0!==o.length||n("Config settings destinations missing or empty");const i={...t,accessToken:a,destinations:o};return{...e,settings:i}}(e);return t},push:async(e,{config:t,mapping:n,data:a,collector:o,env:i})=>await g(e,{config:t,mapping:n,data:a,collector:o,env:i})},U=W;export{p as DestinationDataManager,U as default,W as destinationDataManager,m as examples,w as schemas};//# sourceMappingURL=index.mjs.map
|
|
1
|
+
import{throwError as e}from"@walkeros/core";import{getMappingValue as t,isObject as n}from"@walkeros/core";import{isString as a,isDefined as o}from"@walkeros/core";import{isString as s}from"@walkeros/core";import{getHashServer as r}from"@walkeros/server-core";async function i(e,t="given"){if(!s(e)||!e)return"";let n=e.trim().toLowerCase();if("given"===t){const e=["mr.","mrs.","ms.","miss.","dr.","prof."];for(const t of e)if(n.startsWith(t)){n=n.substring(t.length).trim();break}}if("family"===t){const e=["jr.","sr.","iii","ii","iv","v"];for(const t of e){const e=` ${t}`;if(n.endsWith(e)){n=n.substring(0,n.length-e.length).trim();break}if(n.endsWith(t)){n=n.substring(0,n.length-t.length).trim();break}}}return r(n)}async function c(e){const t=[];if(a(e.email)&&e.email){const n=await async function(e){if(!s(e)||!e)return"";let t=e.trim().toLowerCase();if(t.endsWith("@gmail.com")||t.endsWith("@googlemail.com")){const[e,n]=t.split("@");t=`${e.replace(/\./g,"")}@${n}`}return r(t)}(e.email);n&&t.push({emailAddress:n})}if(a(e.phone)&&e.phone){const n=await async function(e){if(!s(e)||!e)return"";let t=e.trim();const n=t.startsWith("+");return t=t.replace(/\D/g,""),t=n||t.length>10?`+${t}`:`+1${t}`,r(t)}(e.phone);n&&t.push({phoneNumber:n})}if(e.firstName||e.lastName||e.regionCode||e.postalCode){const n={};a(e.firstName)&&e.firstName&&(n.givenName=await i(e.firstName,"given")),a(e.lastName)&&e.lastName&&(n.familyName=await i(e.lastName,"family")),a(e.regionCode)&&e.regionCode&&(n.regionCode=e.regionCode.toUpperCase()),a(e.postalCode)&&e.postalCode&&(n.postalCode=e.postalCode),Object.keys(n).length>0&&t.push({address:n})}if(0!==t.length)return{userIdentifiers:t.slice(0,10)}}async function d(e,t){const n={eventTimestamp:(s=e.timestamp,new Date(s).toISOString())};var s;const r=t||{};a(r.transactionId)&&r.transactionId&&(n.transactionId=r.transactionId.substring(0,512)),a(r.clientId)&&r.clientId&&(n.clientId=r.clientId.substring(0,255)),a(r.userId)&&r.userId&&(n.userId=r.userId.substring(0,256));const i=await c(r);i&&(n.userData=i);const d=function(e){const t={};return a(e.gclid)&&e.gclid&&(t.gclid=e.gclid),a(e.gbraid)&&e.gbraid&&(t.gbraid=e.gbraid),a(e.wbraid)&&e.wbraid&&(t.wbraid=e.wbraid),a(e.sessionAttributes)&&e.sessionAttributes&&(t.sessionAttributes=e.sessionAttributes),Object.keys(t).length>0?t:void 0}(r);d&&(n.adIdentifiers=d),"number"==typeof r.conversionValue&&(n.conversionValue=r.conversionValue),a(r.currency)&&r.currency&&(n.currency=r.currency.substring(0,3).toUpperCase()),r.cartData&&"object"==typeof r.cartData&&(n.cartData=r.cartData),a(r.eventName)&&r.eventName&&(n.eventName=r.eventName.substring(0,40)),a(r.eventSource)&&r.eventSource&&(n.eventSource=r.eventSource);const l={};if("boolean"==typeof r.adUserData&&(l.adUserData=r.adUserData?"CONSENT_GRANTED":"CONSENT_DENIED"),"boolean"==typeof r.adPersonalization&&(l.adPersonalization=r.adPersonalization?"CONSENT_GRANTED":"CONSENT_DENIED"),0===Object.keys(l).length){const t=function(e){if(!e)return;const t={};return o(e.marketing)&&(t.adUserData=e.marketing?"CONSENT_GRANTED":"CONSENT_DENIED"),o(e.personalization)&&(t.adPersonalization=e.personalization?"CONSENT_GRANTED":"CONSENT_DENIED"),Object.keys(t).length>0?t:void 0}(e.consent);t&&(n.consent=t)}else n.consent=l;return n}var l=async function(e,{config:a,mapping:o,data:s,collector:r,env:i}){const{accessToken:c,destinations:l,eventSource:g,validateOnly:u=!1,url:f="https://datamanager.googleapis.com/v1",consent:m,testEventCode:p,logLevel:v="none",userData:b,userId:N,clientId:h,sessionAttributes:y,consentAdUserData:w,consentAdPersonalization:D}=a.settings,I=function(e="none"){const t={debug:0,info:1,warn:2,error:3,none:4}[e];return{debug:(e,n)=>{t<=0&&console.log(`[DataManager] ${e}`,n||"")},info:(e,n)=>{t<=1&&console.log(`[DataManager] ${e}`,n||"")},warn:(e,n)=>{t<=2&&console.warn(`[DataManager] ${e}`,n||"")},error:(e,n)=>{t<=3&&console.error(`[DataManager] ${e}`,n||"")}}}(v),E=b?await t(e,{map:b}):{},C=N?await t(e,N):void 0,S=h?await t(e,h):void 0,O=y?await t(e,y):void 0,k="boolean"==typeof w?w:"string"==typeof w&&e.consent?e.consent[w]:void 0,T="boolean"==typeof D?D:"string"==typeof D&&e.consent?e.consent[D]:void 0,A={};n(E)&&Object.assign(A,E),void 0!==C&&(A.userId=C),void 0!==S&&(A.clientId=S),void 0!==O&&(A.sessionAttributes=O),void 0!==k&&(A.adUserData=k),void 0!==T&&(A.adPersonalization=T);const $=a.data?await t(e,a.data):{},P=n(s)?s:{},j={...A,...n($)?$:{},...P},z=await d(e,j);I.debug("Processing event",{name:e.name,id:e.id,timestamp:e.timestamp}),!z.eventSource&&g&&(z.eventSource=g),!z.consent&&m&&(z.consent=m);const U={events:[z],destinations:l};m&&(U.consent=m),u&&(U.validateOnly=!0),p&&(U.testEventCode=p);const _=(null==i?void 0:i.fetch)||fetch,M=`${f}/events:ingest`;I.debug("Sending to Data Manager API",{endpoint:M,eventCount:U.events.length,destinations:l.length,validateOnly:u});const W=await _(M,{method:"POST",headers:{Authorization:`Bearer ${c}`,"Content-Type":"application/json"},body:JSON.stringify(U)});if(!W.ok){const e=await W.text();throw I.error("API request failed",{status:W.status,error:e}),new Error(`Data Manager API error (${W.status}): ${e}`)}const q=await W.json();if(I.debug("API response",{status:W.status,requestId:q.requestId}),q.validationErrors&&q.validationErrors.length>0)throw I.error("Validation errors",{errors:q.validationErrors}),new Error(`Validation errors: ${JSON.stringify(q.validationErrors)}`);I.info("Event processed successfully",{requestId:q.requestId})},g={},u={type:"datamanager",config:{},async init({config:t}){const n=function(t={}){const n=t.settings||{},{accessToken:a,destinations:o}=n;a||e("Config settings accessToken missing"),o&&0!==o.length||e("Config settings destinations missing or empty");const s={...n,accessToken:a,destinations:o};return{...t,settings:s}}(t);return n},push:async(e,{config:t,mapping:n,data:a,collector:o,env:s})=>await l(e,{config:t,mapping:n,data:a,collector:o,env:s})},f=u;export{g as DestinationDataManager,f as default,u as destinationDataManager};//# sourceMappingURL=index.mjs.map
|