@walkeros/server-destination-amplitude 4.8.1 → 4.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +1 -1
- package/dist/dev.d.mts +24 -7
- package/dist/dev.d.ts +24 -7
- package/dist/dev.js +1 -1
- package/dist/dev.js.map +1 -1
- package/dist/dev.mjs +1 -1
- package/dist/dev.mjs.map +1 -1
- package/dist/examples/index.d.mts +23 -6
- package/dist/examples/index.d.ts +23 -6
- package/dist/examples/index.js +71 -29
- package/dist/examples/index.mjs +72 -30
- package/dist/index.d.mts +16 -6
- package/dist/index.d.ts +16 -6
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/walkerOS.json +154 -26
- package/package.json +5 -4
package/dist/index.d.mts
CHANGED
|
@@ -9,15 +9,22 @@ import { DestinationServer } from '@walkeros/server-core';
|
|
|
9
9
|
* Key difference from web: no setUserId/setDeviceId/setSessionId.
|
|
10
10
|
* Identity is per-event via EventOptions on every SDK call.
|
|
11
11
|
*/
|
|
12
|
+
/**
|
|
13
|
+
* What an SDK call answers: a promise of its result, which resolves after
|
|
14
|
+
* the event was sent (with its HTTP `code`) instead of rejecting.
|
|
15
|
+
*/
|
|
16
|
+
interface AmplitudeReturn {
|
|
17
|
+
promise: Promise<unknown>;
|
|
18
|
+
}
|
|
12
19
|
interface AmplitudeSDK {
|
|
13
20
|
init: (apiKey: string, options?: Record<string, unknown>) => {
|
|
14
21
|
promise: Promise<void>;
|
|
15
22
|
};
|
|
16
|
-
track: (eventType: string, eventProperties?: Record<string, unknown>, eventOptions?: EventOptions) =>
|
|
17
|
-
identify: (identify: IdentifyInstance, eventOptions?: EventOptions) =>
|
|
18
|
-
revenue: (revenue: RevenueInstance, eventOptions?: EventOptions) =>
|
|
19
|
-
setGroup: (groupType: string, groupName: string | string[], eventOptions?: EventOptions) =>
|
|
20
|
-
groupIdentify: (groupType: string, groupName: string, identify: IdentifyInstance, eventOptions?: EventOptions) =>
|
|
23
|
+
track: (eventType: string, eventProperties?: Record<string, unknown>, eventOptions?: EventOptions) => AmplitudeReturn;
|
|
24
|
+
identify: (identify: IdentifyInstance, eventOptions?: EventOptions) => AmplitudeReturn;
|
|
25
|
+
revenue: (revenue: RevenueInstance, eventOptions?: EventOptions) => AmplitudeReturn;
|
|
26
|
+
setGroup: (groupType: string, groupName: string | string[], eventOptions?: EventOptions) => AmplitudeReturn;
|
|
27
|
+
groupIdentify: (groupType: string, groupName: string, identify: IdentifyInstance, eventOptions?: EventOptions) => AmplitudeReturn;
|
|
21
28
|
setOptOut: (optOut: boolean) => void;
|
|
22
29
|
flush: () => {
|
|
23
30
|
promise: Promise<void>;
|
|
@@ -136,11 +143,13 @@ type Config = {
|
|
|
136
143
|
} & DestinationServer.Config<Types>;
|
|
137
144
|
type InitFn = DestinationServer.InitFn<Types>;
|
|
138
145
|
type PushFn = DestinationServer.PushFn<Types>;
|
|
146
|
+
type PushBatchFn = Destination$1.PushBatchFn<Types>;
|
|
139
147
|
type PartialConfig = DestinationServer.PartialConfig<Types>;
|
|
140
148
|
type PushEvents = DestinationServer.PushEvents<Mapping>;
|
|
141
149
|
type Rule = Mapping$1.Rule<Mapping>;
|
|
142
150
|
type Rules = Mapping$1.Rules<Rule>;
|
|
143
151
|
|
|
152
|
+
type index_AmplitudeReturn = AmplitudeReturn;
|
|
144
153
|
type index_AmplitudeSDK = AmplitudeSDK;
|
|
145
154
|
type index_Config = Config;
|
|
146
155
|
type index_Destination = Destination;
|
|
@@ -151,6 +160,7 @@ type index_InitFn = InitFn;
|
|
|
151
160
|
type index_InitSettings = InitSettings;
|
|
152
161
|
type index_Mapping = Mapping;
|
|
153
162
|
type index_PartialConfig = PartialConfig;
|
|
163
|
+
type index_PushBatchFn = PushBatchFn;
|
|
154
164
|
type index_PushEvents = PushEvents;
|
|
155
165
|
type index_PushFn = PushFn;
|
|
156
166
|
type index_RevenueInstance = RevenueInstance;
|
|
@@ -159,7 +169,7 @@ type index_Rules = Rules;
|
|
|
159
169
|
type index_Settings = Settings;
|
|
160
170
|
type index_Types = Types;
|
|
161
171
|
declare namespace index {
|
|
162
|
-
export type { index_AmplitudeSDK as AmplitudeSDK, index_Config as Config, index_Destination as Destination, index_Env as Env, index_EventOptions as EventOptions, index_IdentifyInstance as IdentifyInstance, index_InitFn as InitFn, index_InitSettings as InitSettings, index_Mapping as Mapping, index_PartialConfig as PartialConfig, index_PushEvents as PushEvents, index_PushFn as PushFn, index_RevenueInstance as RevenueInstance, index_Rule as Rule, index_Rules as Rules, index_Settings as Settings, index_Types as Types };
|
|
172
|
+
export type { index_AmplitudeReturn as AmplitudeReturn, index_AmplitudeSDK as AmplitudeSDK, index_Config as Config, index_Destination as Destination, index_Env as Env, index_EventOptions as EventOptions, index_IdentifyInstance as IdentifyInstance, index_InitFn as InitFn, index_InitSettings as InitSettings, index_Mapping as Mapping, index_PartialConfig as PartialConfig, index_PushBatchFn as PushBatchFn, index_PushEvents as PushEvents, index_PushFn as PushFn, index_RevenueInstance as RevenueInstance, index_Rule as Rule, index_Rules as Rules, index_Settings as Settings, index_Types as Types };
|
|
163
173
|
}
|
|
164
174
|
|
|
165
175
|
declare const destinationAmplitude: Destination;
|
package/dist/index.d.ts
CHANGED
|
@@ -9,15 +9,22 @@ import { DestinationServer } from '@walkeros/server-core';
|
|
|
9
9
|
* Key difference from web: no setUserId/setDeviceId/setSessionId.
|
|
10
10
|
* Identity is per-event via EventOptions on every SDK call.
|
|
11
11
|
*/
|
|
12
|
+
/**
|
|
13
|
+
* What an SDK call answers: a promise of its result, which resolves after
|
|
14
|
+
* the event was sent (with its HTTP `code`) instead of rejecting.
|
|
15
|
+
*/
|
|
16
|
+
interface AmplitudeReturn {
|
|
17
|
+
promise: Promise<unknown>;
|
|
18
|
+
}
|
|
12
19
|
interface AmplitudeSDK {
|
|
13
20
|
init: (apiKey: string, options?: Record<string, unknown>) => {
|
|
14
21
|
promise: Promise<void>;
|
|
15
22
|
};
|
|
16
|
-
track: (eventType: string, eventProperties?: Record<string, unknown>, eventOptions?: EventOptions) =>
|
|
17
|
-
identify: (identify: IdentifyInstance, eventOptions?: EventOptions) =>
|
|
18
|
-
revenue: (revenue: RevenueInstance, eventOptions?: EventOptions) =>
|
|
19
|
-
setGroup: (groupType: string, groupName: string | string[], eventOptions?: EventOptions) =>
|
|
20
|
-
groupIdentify: (groupType: string, groupName: string, identify: IdentifyInstance, eventOptions?: EventOptions) =>
|
|
23
|
+
track: (eventType: string, eventProperties?: Record<string, unknown>, eventOptions?: EventOptions) => AmplitudeReturn;
|
|
24
|
+
identify: (identify: IdentifyInstance, eventOptions?: EventOptions) => AmplitudeReturn;
|
|
25
|
+
revenue: (revenue: RevenueInstance, eventOptions?: EventOptions) => AmplitudeReturn;
|
|
26
|
+
setGroup: (groupType: string, groupName: string | string[], eventOptions?: EventOptions) => AmplitudeReturn;
|
|
27
|
+
groupIdentify: (groupType: string, groupName: string, identify: IdentifyInstance, eventOptions?: EventOptions) => AmplitudeReturn;
|
|
21
28
|
setOptOut: (optOut: boolean) => void;
|
|
22
29
|
flush: () => {
|
|
23
30
|
promise: Promise<void>;
|
|
@@ -136,11 +143,13 @@ type Config = {
|
|
|
136
143
|
} & DestinationServer.Config<Types>;
|
|
137
144
|
type InitFn = DestinationServer.InitFn<Types>;
|
|
138
145
|
type PushFn = DestinationServer.PushFn<Types>;
|
|
146
|
+
type PushBatchFn = Destination$1.PushBatchFn<Types>;
|
|
139
147
|
type PartialConfig = DestinationServer.PartialConfig<Types>;
|
|
140
148
|
type PushEvents = DestinationServer.PushEvents<Mapping>;
|
|
141
149
|
type Rule = Mapping$1.Rule<Mapping>;
|
|
142
150
|
type Rules = Mapping$1.Rules<Rule>;
|
|
143
151
|
|
|
152
|
+
type index_AmplitudeReturn = AmplitudeReturn;
|
|
144
153
|
type index_AmplitudeSDK = AmplitudeSDK;
|
|
145
154
|
type index_Config = Config;
|
|
146
155
|
type index_Destination = Destination;
|
|
@@ -151,6 +160,7 @@ type index_InitFn = InitFn;
|
|
|
151
160
|
type index_InitSettings = InitSettings;
|
|
152
161
|
type index_Mapping = Mapping;
|
|
153
162
|
type index_PartialConfig = PartialConfig;
|
|
163
|
+
type index_PushBatchFn = PushBatchFn;
|
|
154
164
|
type index_PushEvents = PushEvents;
|
|
155
165
|
type index_PushFn = PushFn;
|
|
156
166
|
type index_RevenueInstance = RevenueInstance;
|
|
@@ -159,7 +169,7 @@ type index_Rules = Rules;
|
|
|
159
169
|
type index_Settings = Settings;
|
|
160
170
|
type index_Types = Types;
|
|
161
171
|
declare namespace index {
|
|
162
|
-
export type { index_AmplitudeSDK as AmplitudeSDK, index_Config as Config, index_Destination as Destination, index_Env as Env, index_EventOptions as EventOptions, index_IdentifyInstance as IdentifyInstance, index_InitFn as InitFn, index_InitSettings as InitSettings, index_Mapping as Mapping, index_PartialConfig as PartialConfig, index_PushEvents as PushEvents, index_PushFn as PushFn, index_RevenueInstance as RevenueInstance, index_Rule as Rule, index_Rules as Rules, index_Settings as Settings, index_Types as Types };
|
|
172
|
+
export type { index_AmplitudeReturn as AmplitudeReturn, index_AmplitudeSDK as AmplitudeSDK, index_Config as Config, index_Destination as Destination, index_Env as Env, index_EventOptions as EventOptions, index_IdentifyInstance as IdentifyInstance, index_InitFn as InitFn, index_InitSettings as InitSettings, index_Mapping as Mapping, index_PartialConfig as PartialConfig, index_PushBatchFn as PushBatchFn, index_PushEvents as PushEvents, index_PushFn as PushFn, index_RevenueInstance as RevenueInstance, index_Rule as Rule, index_Rules as Rules, index_Settings as Settings, index_Types as Types };
|
|
163
173
|
}
|
|
164
174
|
|
|
165
175
|
declare const destinationAmplitude: Destination;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var mod,__create=Object.create,__defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__getProtoOf=Object.getPrototypeOf,__hasOwnProp=Object.prototype.hasOwnProperty,__copyProps=(to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},index_exports={};((target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})})(index_exports,{DestinationAmplitude:()=>types_exports,default:()=>index_default,destinationAmplitude:()=>destinationAmplitude}),module.exports=(mod=index_exports,__copyProps(__defProp({},"__esModule",{value:!0}),mod));var import_core=require("@walkeros/core"),amplitudeSDK=((mod,isNodeMode,target)=>(target=null!=mod?__create(__getProtoOf(mod)):{},__copyProps(!isNodeMode&&mod&&mod.__esModule?target:__defProp(target,"default",{value:mod,enumerable:!0}),mod)))(require("@amplitude/analytics-node")),IDENTIFY_OP_KEYS=["set","setOnce","add","append","prepend","preInsert","postInsert","remove"],IDENTITY_KEYS=["user_id","device_id","session_id"],REVENUE_FIELD_MAP={productId:"setProductId",price:"setPrice",quantity:"setQuantity",revenueType:"setRevenueType",currency:"setCurrency",revenue:"setRevenue",receipt:"setReceipt",receiptSig:"setReceiptSig"};function getAmplitude(env){const amplitude=env?.amplitude;return amplitude??amplitudeSDK}function buildIdentify(amp,resolved){let identify;const ensure=()=>(identify||(identify=new amp.Identify),identify);for(const op of IDENTIFY_OP_KEYS){const bag=resolved[op];if((0,import_core.isObject)(bag))for(const[prop,value]of Object.entries(bag))(0,import_core.isDefined)(value)&&("add"===op?(0,import_core.isNumber)(value)&&ensure().add(prop,value):ensure()[op](prop,value))}if((0,import_core.isArray)(resolved.unset))for(const prop of resolved.unset)(0,import_core.isString)(prop)&&ensure().unset(prop);return!0===resolved.clearAll&&ensure().clearAll(),identify}function buildRevenue(amp,item){const rev=new amp.Revenue;for(const[key,setter]of Object.entries(REVENUE_FIELD_MAP)){const value=item[key];(0,import_core.isDefined)(value)&&rev[setter](value)}return(0,import_core.isObject)(item.eventProperties)&&rev.setEventProperties(item.eventProperties),rev}function buildEventOptions(identityResolved,eventOptionsResolved){const opts={};if(identityResolved&&((0,import_core.isString)(identityResolved.user_id)&&(opts.user_id=identityResolved.user_id),(0,import_core.isString)(identityResolved.device_id)&&(opts.device_id=identityResolved.device_id),void 0!==identityResolved.session_id)){const session=identityResolved.session_id;opts.session_id=(0,import_core.isNumber)(session)?session:(0,import_core.isString)(session)&&/^\d+$/.test(session)?Number(session):(0,import_core.isString)(session)?function(value){let hash=5381;for(let i=0;i<value.length;i++)hash=(hash<<5)+hash+value.charCodeAt(i)|0;return Math.abs(hash)}(session):void 0}if(eventOptionsResolved)for(const[key,value]of Object.entries(eventOptionsResolved))(0,import_core.isDefined)(value)&&!IDENTITY_KEYS.includes(key)&&(opts[key]=value);return opts}var types_exports={},destinationAmplitude={type:"amplitude",config:{},async init({config:partialConfig,env:env,logger:logger}){const config=function(partialConfig={},logger){const settings=partialConfig.settings||{},{apiKey:apiKey}=settings;apiKey||logger.throw("Config settings apiKey missing");const settingsConfig={...settings,apiKey:apiKey};return{...partialConfig,settings:settingsConfig}}(partialConfig,logger),amp=getAmplitude(env),{apiKey:apiKey,identify:_identify,eventOptions:_eventOptions,include:_include,...nodeOptions}=config.settings;return await amp.init(apiKey,nodeOptions).promise,config},push:async(event,context)=>await async function(event,{config:config,rule:rule,data:data,collector:collector,env:env}){const amp=getAmplitude(env),settings=config.settings,mappingSettings=rule?.settings||{},identifyMapping=mappingSettings.identify??settings.identify;let identifyResolved;if(void 0!==identifyMapping){const resolved=await(0,import_core.getMappingValue)(event,identifyMapping,{collector:collector});(0,import_core.isObject)(resolved)&&(identifyResolved=resolved)}const eventOptionsMapping=mappingSettings.eventOptions??settings.eventOptions;let eventOptionsResolved;if(void 0!==eventOptionsMapping){const resolved=await(0,import_core.getMappingValue)(event,eventOptionsMapping,{collector:collector});(0,import_core.isObject)(resolved)&&(eventOptionsResolved=resolved)}const eventOptions=buildEventOptions(identifyResolved,eventOptionsResolved),hasEventOptions=Object.keys(eventOptions).length>0;if(identifyResolved){const id=buildIdentify(amp,identifyResolved);id&&.identify(id,hasEventOptions?eventOptions:void 0)}if(void 0!==mappingSettings.group){const resolved=await(0,import_core.getMappingValue)(event,mappingSettings.group,{collector:collector});if((0,import_core.isObject)(resolved)){const{type:type,name:name}=resolved;(0,import_core.isString)(type)&&((0,import_core.isString)(name)||(0,import_core.isArray)(name))&&.setGroup(type,name,hasEventOptions?eventOptions:void 0)}}if(void 0!==mappingSettings.groupIdentify){const resolved=await(0,import_core.getMappingValue)(event,mappingSettings.groupIdentify,{collector:collector});if((0,import_core.isObject)(resolved)){const{type:type,name:name}=resolved;if((0,import_core.isString)(type)&&(0,import_core.isString)(name)){const id=buildIdentify(amp,resolved);id&&.groupIdentify(type,name,id,hasEventOptions?eventOptions:void 0)}}}if(void 0!==mappingSettings.revenue){const resolved=await(0,import_core.getMappingValue)(event,mappingSettings.revenue,{collector:collector}),items=(0,import_core.isArray)(resolved)?resolved:(0,import_core.isObject)(resolved)?[resolved]:[];for(const item of items)(0,import_core.isObject)(item)&&.revenue(buildRevenue(amp,item),hasEventOptions?eventOptions:void 0)}if(!0!==rule?.silent){const eventType=(0,import_core.isString)(rule?.name)?rule.name:event.name,include=mappingSettings.include??settings.include,eventProperties=include?(0,import_core.flattenIncludeSections)(event,include):(0,import_core.isObject)(data)?data:{};amp.track(eventType,eventProperties,hasEventOptions?eventOptions:void 0)}}(event,context),on(type,context){if("consent"!==type||!context.data)return;const amp=getAmplitude(context.env),consent=context.data,required=context.config?.consent;if(!required||0===Object.keys(required).length)return;const allGranted=Object.keys(required).every(key=>!0===consent[key]);amp.setOptOut(!allGranted)},async destroy({env:env}){const amp=getAmplitude(env);await amp.flush().promise}},index_default=destinationAmplitude;//# sourceMappingURL=index.js.map
|
|
1
|
+
"use strict";var mod,__create=Object.create,__defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__getProtoOf=Object.getPrototypeOf,__hasOwnProp=Object.prototype.hasOwnProperty,__copyProps=(to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},index_exports={};((target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})})(index_exports,{DestinationAmplitude:()=>types_exports,default:()=>index_default,destinationAmplitude:()=>destinationAmplitude}),module.exports=(mod=index_exports,__copyProps(__defProp({},"__esModule",{value:!0}),mod));var import_server_core3=require("@walkeros/server-core"),import_core=require("@walkeros/core");var import_core2=require("@walkeros/core"),import_server_core=require("@walkeros/server-core"),amplitudeSDK=((mod,isNodeMode,target)=>(target=null!=mod?__create(__getProtoOf(mod)):{},__copyProps(!isNodeMode&&mod&&mod.__esModule?target:__defProp(target,"default",{value:mod,enumerable:!0}),mod)))(require("@amplitude/analytics-node")),IDENTIFY_OP_KEYS=["set","setOnce","add","append","prepend","preInsert","postInsert","remove"],IDENTITY_KEYS=["user_id","device_id","session_id"],REVENUE_FIELD_MAP={productId:"setProductId",price:"setPrice",quantity:"setQuantity",revenueType:"setRevenueType",currency:"setCurrency",revenue:"setRevenue",receipt:"setReceipt",receiptSig:"setReceiptSig"};function getAmplitude(env){const injected=(0,import_core2.isObject)(env)?env.amplitude:void 0;return value=injected,(0,import_core2.isObject)(value)&&"function"==typeof value.track?injected:amplitudeSDK;var value}async function call(message,send){let result;try{const answer=await send();result=(0,import_core2.isObject)(answer)&&(0,import_core2.isThenable)(answer.promise)?await answer.promise:answer}catch(error){throw new import_core2.StepError(message,{code:"transport",cause:error})}const code=(0,import_core2.isObject)(result)&&(0,import_core2.isNumber)(result.code)?result.code:void 0;if(void 0!==code&&(code<200||code>=300))throw new import_core2.StepError(`${message} (code ${code})`,{code:"transport",status:code||void 0})}function buildIdentify(amp,resolved){let identify;const ensure=()=>(identify||(identify=new amp.Identify),identify);for(const op of IDENTIFY_OP_KEYS){const bag=resolved[op];if((0,import_core2.isObject)(bag))for(const[prop,value]of Object.entries(bag))(0,import_core2.isDefined)(value)&&("add"===op?(0,import_core2.isNumber)(value)&&ensure().add(prop,value):ensure()[op](prop,value))}if((0,import_core2.isArray)(resolved.unset))for(const prop of resolved.unset)(0,import_core2.isString)(prop)&&ensure().unset(prop);return!0===resolved.clearAll&&ensure().clearAll(),identify}function buildRevenue(amp,item){const rev=new amp.Revenue;for(const[key,setter]of Object.entries(REVENUE_FIELD_MAP)){const value=item[key];(0,import_core2.isDefined)(value)&&rev[setter](value)}return(0,import_core2.isObject)(item.eventProperties)&&rev.setEventProperties(item.eventProperties),rev}function buildEventOptions(identityResolved,eventOptionsResolved){const opts={};if(identityResolved&&((0,import_core2.isString)(identityResolved.user_id)&&(opts.user_id=identityResolved.user_id),(0,import_core2.isString)(identityResolved.device_id)&&(opts.device_id=identityResolved.device_id),void 0!==identityResolved.session_id)){const session=identityResolved.session_id;opts.session_id=(0,import_core2.isNumber)(session)?session:(0,import_core2.isString)(session)&&/^\d+$/.test(session)?Number(session):(0,import_core2.isString)(session)?function(value){let hash=5381;for(let i=0;i<value.length;i++)hash=(hash<<5)+hash+value.charCodeAt(i)|0;return Math.abs(hash)}(session):void 0}if(eventOptionsResolved)for(const[key,value]of Object.entries(eventOptionsResolved))(0,import_core2.isDefined)(value)&&!IDENTITY_KEYS.includes(key)&&(opts[key]=value);return opts}function skipped(logger,event,call2,key){logger.debug(`Amplitude skipped ${call2}: ${key} resolved to nothing`,{event:event.name})}function held(sent){return sent.catch(()=>{}),sent}async function delivered(calls){for(const result of await calls)if("rejected"===result.status)throw result.reason}async function enqueue(amp,event,rule,data,settings,collector,logger){const mappingSettings=rule?.settings||{},calls=[],identifyMapping=mappingSettings.identify??settings.identify;let identifyResolved;if(void 0!==identifyMapping){const resolved=await(0,import_core2.getMappingValue)(event,identifyMapping,{collector:collector});(0,import_core2.isObject)(resolved)?identifyResolved=resolved:skipped(logger,event,"identify","identify")}const eventOptionsMapping=mappingSettings.eventOptions??settings.eventOptions;let eventOptionsResolved;if(void 0!==eventOptionsMapping){const resolved=await(0,import_core2.getMappingValue)(event,eventOptionsMapping,{collector:collector});(0,import_core2.isObject)(resolved)&&(eventOptionsResolved=resolved)}const eventOptions=buildEventOptions(identifyResolved,eventOptionsResolved),hasEventOptions=Object.keys(eventOptions).length>0,optionsFor=type=>{if(!event.id)return hasEventOptions?eventOptions:void 0;return{insert_id:type?`${event.id}-${type}`:event.id,...eventOptions}};if(identifyResolved){const id=buildIdentify(amp,identifyResolved);id&&calls.push(held(call("Amplitude identify failed",()=>amp.identify(id,optionsFor("identify")))))}if(void 0!==mappingSettings.group){const resolved=await(0,import_core2.getMappingValue)(event,mappingSettings.group,{collector:collector}),type=(0,import_core2.isObject)(resolved)?resolved.type:void 0,name=(0,import_core2.isObject)(resolved)?resolved.name:void 0;(0,import_core2.isString)(type)&&((0,import_core2.isString)(name)||(0,import_core2.isArray)(name)&&name.every(import_core2.isString))?calls.push(held(call("Amplitude setGroup failed",()=>amp.setGroup(type,name,optionsFor("group"))))):skipped(logger,event,"group","group")}if(void 0!==mappingSettings.groupIdentify){const resolved=await(0,import_core2.getMappingValue)(event,mappingSettings.groupIdentify,{collector:collector}),type=(0,import_core2.isObject)(resolved)?resolved.type:void 0,name=(0,import_core2.isObject)(resolved)?resolved.name:void 0,id=(0,import_core2.isObject)(resolved)&&(0,import_core2.isString)(type)&&(0,import_core2.isString)(name)?buildIdentify(amp,resolved):void 0;id&&(0,import_core2.isString)(type)&&(0,import_core2.isString)(name)?calls.push(held(call("Amplitude groupIdentify failed",()=>amp.groupIdentify(type,name,id,optionsFor("groupIdentify"))))):skipped(logger,event,"groupIdentify","groupIdentify")}if(void 0!==mappingSettings.revenue){const resolved=await(0,import_core2.getMappingValue)(event,mappingSettings.revenue,{collector:collector}),items=(0,import_core2.isArray)(resolved)?resolved:(0,import_core2.isObject)(resolved)?[resolved]:[];items.some(import_core2.isObject)||skipped(logger,event,"revenue","revenue");for(const[index,item]of items.entries())(0,import_core2.isObject)(item)&&calls.push(held(call("Amplitude revenue failed",()=>amp.revenue(buildRevenue(amp,item),optionsFor(`revenue-${index}`)))))}if(!0!==rule?.silent){const eventType=(0,import_core2.isString)(rule?.name)?rule.name:event.name,include=mappingSettings.include??settings.include,eventProperties=include?(0,import_core2.flattenIncludeSections)(event,include):(0,import_core2.isObject)(data)?data:{};calls.push(held(call("Amplitude track failed",()=>amp.track(eventType,eventProperties,optionsFor()))))}return calls}async function flush(amp){await amp.flush().promise}function settingsOf(settings){if(!settings)throw new import_core2.StepError("Amplitude settings missing, init() not run",{code:"not-ready"});return settings}var import_analytics_core=require("@amplitude/analytics-core"),import_core3=require("@walkeros/core"),import_server_core2=require("@walkeros/server-core"),RETRIED=[import_analytics_core.Status.Failed,import_analytics_core.Status.Timeout,import_analytics_core.Status.Unknown],AmplitudeTransport=class extends import_analytics_core.BaseTransport{constructor(timeout){super(),this.timeout=timeout}timeout;async send(serverUrl,payload){let body,status=200;try{({data:body}=await(0,import_server_core2.sendServer)(serverUrl,JSON.stringify(payload),{timeout:this.timeout}))}catch(error){if(!(error instanceof import_core3.StepError)||void 0===error.status)throw error;status=error.status,body=error.body}const response=this.buildResponse({code:status,...(0,import_core3.isObject)(body)?body:{}});if(!response||RETRIED.includes(response.status))throw new import_core3.StepError(`Amplitude answered ${status}`,{code:"transport",status:status});return response}},types_exports={};function amplitudeWaits(retries,wait){return retries&&wait+500*retries*(retries-1)}var destinationAmplitude={type:"amplitude",config:{batch:{size:200,wait:1e4}},async init({config:partialConfig,env:env}){const config=function(partialConfig={}){const settings=partialConfig.settings||{},{apiKey:apiKey}=settings;if(!apiKey)throw new import_core.StepError("Config settings apiKey missing",{code:"config"});const settingsConfig={...settings,apiKey:apiKey};return{...partialConfig,settings:settingsConfig}}(partialConfig),amp=getAmplitude(env),{apiKey:apiKey,identify:_identify,eventOptions:_eventOptions,include:_include,...nodeOptions}=config.settings,budget=(0,import_server_core3.retryBudget)(partialConfig.timeout,amplitudeWaits);return await amp.init(apiKey,{transportProvider:new AmplitudeTransport(budget.attempt),flushMaxRetries:budget.retries+1,flushIntervalMillis:budget.wait,...nodeOptions}).promise,config},push:async function(event,{config:config,rule:rule,data:data,collector:collector,env:env,logger:logger}){const amp=getAmplitude(env),settings=settingsOf(config.settings),calls=await enqueue(amp,event,rule,data,settings,collector,logger);if(calls.length){const settled=Promise.allSettled(calls);await flush(amp),await delivered(settled)}},pushBatch:async function(batch,{config:config,collector:collector,env:env,logger:logger}){const amp=getAmplitude(env),settings=settingsOf(config.settings),outcomes=[];for(const entry of batch.entries)try{const calls=await enqueue(amp,entry.event,entry.rule,entry.data,settings,collector,logger);outcomes.push({settled:Promise.allSettled(calls)})}catch(error){outcomes.push({error:error})}await flush(amp);const failed=[];for(const[index,{settled:settled,error:error}]of outcomes.entries())settled?await delivered(settled).catch(reason=>failed.push({index:index,error:reason})):failed.push({index:index,error:error});return(0,import_server_core.batchOutcome)(batch.entries.length,failed)},on(type,context){if("consent"!==type||!context.data)return;const amp=getAmplitude(context.env),consent=context.data,required=context.config?.consent;if(!required||0===Object.keys(required).length)return;const allGranted=Object.keys(required).every(key=>!0===consent[key]);amp.setOptOut(!allGranted)},async destroy({env:env}){const amp=getAmplitude(env);await amp.flush().promise}},index_default=destinationAmplitude;//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/push.ts","../src/types/index.ts"],"sourcesContent":["import type { WalkerOS } from '@walkeros/core';\nimport type { Destination, Env } from './types';\nimport { getConfig } from './config';\nimport { push, getAmplitude } from './push';\n\nexport * as DestinationAmplitude from './types';\n\nexport const destinationAmplitude: Destination = {\n type: 'amplitude',\n\n config: {},\n\n async init({ config: partialConfig, env, logger }) {\n const config = getConfig(partialConfig, logger);\n const amp = getAmplitude(env as Record<string, unknown> | undefined);\n\n // Destructure walkerOS-specific keys; the rest flow through to the SDK.\n const {\n apiKey,\n identify: _identify,\n eventOptions: _eventOptions,\n include: _include,\n ...nodeOptions\n } = config.settings;\n\n await amp.init(apiKey, nodeOptions).promise;\n return config;\n },\n\n async push(event, context) {\n return await push(event, context);\n },\n\n on(type, context) {\n if (type !== 'consent' || !context.data) return;\n const amp = getAmplitude(\n context.env as Record<string, unknown> | undefined,\n );\n\n const consent = context.data as WalkerOS.Consent;\n const required = context.config?.consent;\n if (!required || Object.keys(required).length === 0) return;\n\n const allGranted = Object.keys(required).every(\n (key) => consent[key] === true,\n );\n amp.setOptOut(!allGranted);\n },\n\n async destroy({ env }) {\n const amp = getAmplitude(env as Record<string, unknown> | undefined);\n await amp.flush().promise;\n },\n};\n\nexport default destinationAmplitude;\n","import type { Config, Settings, PartialConfig } from './types';\nimport type { Logger } from '@walkeros/core';\n\nexport function getConfig(\n partialConfig: PartialConfig = {},\n logger: Logger.Instance,\n): Config {\n const settings = (partialConfig.settings || {}) as Partial<Settings>;\n const { apiKey } = settings;\n\n if (!apiKey) logger.throw('Config settings apiKey missing');\n\n const settingsConfig: Settings = {\n ...settings,\n apiKey,\n };\n\n return { ...partialConfig, settings: settingsConfig };\n}\n","import type {\n PushFn,\n AmplitudeSDK,\n EventOptions,\n IdentifyInstance,\n} from './types';\nimport {\n getMappingValue,\n isArray,\n isDefined,\n isNumber,\n isObject,\n isString,\n flattenIncludeSections,\n} from '@walkeros/core';\nimport * as amplitudeSDK from '@amplitude/analytics-node';\n\ntype IdentifyOpKey =\n | 'set'\n | 'setOnce'\n | 'add'\n | 'append'\n | 'prepend'\n | 'preInsert'\n | 'postInsert'\n | 'remove';\n\nconst IDENTIFY_OP_KEYS: IdentifyOpKey[] = [\n 'set',\n 'setOnce',\n 'add',\n 'append',\n 'prepend',\n 'preInsert',\n 'postInsert',\n 'remove',\n];\n\n/** Identity keys that map to EventOptions fields (not Identify operations). */\nconst IDENTITY_KEYS = ['user_id', 'device_id', 'session_id'] as const;\n\ntype RevenueFieldSetter =\n | 'setProductId'\n | 'setPrice'\n | 'setQuantity'\n | 'setRevenueType'\n | 'setCurrency'\n | 'setRevenue'\n | 'setReceipt'\n | 'setReceiptSig';\n\nconst REVENUE_FIELD_MAP: Record<string, RevenueFieldSetter> = {\n productId: 'setProductId',\n price: 'setPrice',\n quantity: 'setQuantity',\n revenueType: 'setRevenueType',\n currency: 'setCurrency',\n revenue: 'setRevenue',\n receipt: 'setReceipt',\n receiptSig: 'setReceiptSig',\n};\n\nexport function getAmplitude(\n env: Record<string, unknown> | undefined,\n): AmplitudeSDK {\n const amplitude = env?.amplitude as AmplitudeSDK | undefined;\n return amplitude ?? (amplitudeSDK as unknown as AmplitudeSDK);\n}\n\n/**\n * Deterministic string -> positive integer hash (djb2).\n * Used for session_id when walkerOS provides a string session.\n */\nfunction hashStringToInt(value: string): number {\n let hash = 5381;\n for (let i = 0; i < value.length; i++) {\n hash = ((hash << 5) + hash + value.charCodeAt(i)) | 0;\n }\n return Math.abs(hash);\n}\n\nfunction buildIdentify(\n amp: AmplitudeSDK,\n resolved: Record<string, unknown>,\n): IdentifyInstance | undefined {\n let identify: IdentifyInstance | undefined;\n const ensure = () => {\n if (!identify) identify = new amp.Identify();\n return identify;\n };\n\n for (const op of IDENTIFY_OP_KEYS) {\n const bag = resolved[op];\n if (!isObject(bag)) continue;\n for (const [prop, value] of Object.entries(bag)) {\n if (!isDefined(value)) continue;\n if (op === 'add') {\n if (isNumber(value)) ensure().add(prop, value);\n } else {\n ensure()[op](prop, value);\n }\n }\n }\n\n if (isArray(resolved.unset)) {\n for (const prop of resolved.unset) {\n if (isString(prop)) ensure().unset(prop);\n }\n }\n\n if (resolved.clearAll === true) {\n ensure().clearAll();\n }\n\n return identify;\n}\n\nfunction buildRevenue(\n amp: AmplitudeSDK,\n item: Record<string, unknown>,\n): InstanceType<AmplitudeSDK['Revenue']> {\n const rev = new amp.Revenue();\n for (const [key, setter] of Object.entries(REVENUE_FIELD_MAP)) {\n const value = item[key];\n if (!isDefined(value)) continue;\n (rev[setter] as (v: unknown) => unknown)(value);\n }\n if (isObject(item.eventProperties)) {\n rev.setEventProperties(item.eventProperties as Record<string, unknown>);\n }\n return rev;\n}\n\n/**\n * Build EventOptions from resolved identity and eventOptions mappings.\n */\nfunction buildEventOptions(\n identityResolved?: Record<string, unknown>,\n eventOptionsResolved?: Record<string, unknown>,\n): EventOptions {\n const opts: EventOptions = {};\n\n if (identityResolved) {\n if (isString(identityResolved.user_id))\n opts.user_id = identityResolved.user_id;\n if (isString(identityResolved.device_id))\n opts.device_id = identityResolved.device_id;\n if (identityResolved.session_id !== undefined) {\n const session = identityResolved.session_id;\n opts.session_id = isNumber(session)\n ? session\n : isString(session) && /^\\d+$/.test(session)\n ? Number(session)\n : isString(session)\n ? hashStringToInt(session)\n : undefined;\n }\n }\n\n if (eventOptionsResolved) {\n // Copy all recognized EventOptions fields\n for (const [key, value] of Object.entries(eventOptionsResolved)) {\n if (\n isDefined(value) &&\n !IDENTITY_KEYS.includes(key as (typeof IDENTITY_KEYS)[number])\n ) {\n (opts as Record<string, unknown>)[key] = value;\n }\n }\n }\n\n return opts;\n}\n\nexport const push: PushFn = async function (\n event,\n { config, rule, data, collector, env },\n) {\n const amp = getAmplitude(env as Record<string, unknown> | undefined);\n const settings = config.settings!;\n const mappingSettings = rule?.settings || {};\n\n // 1. Resolve identity -- rule-level override wins over destination-level.\n const identifyMapping = mappingSettings.identify ?? settings.identify;\n let identifyResolved: Record<string, unknown> | undefined;\n if (identifyMapping !== undefined) {\n const resolved = await getMappingValue(event, identifyMapping, {\n collector,\n });\n if (isObject(resolved)) {\n identifyResolved = resolved as Record<string, unknown>;\n }\n }\n\n // 2. Resolve eventOptions -- rule-level override wins over destination-level.\n const eventOptionsMapping =\n mappingSettings.eventOptions ?? settings.eventOptions;\n let eventOptionsResolved: Record<string, unknown> | undefined;\n if (eventOptionsMapping !== undefined) {\n const resolved = await getMappingValue(event, eventOptionsMapping, {\n collector,\n });\n if (isObject(resolved)) {\n eventOptionsResolved = resolved as Record<string, unknown>;\n }\n }\n\n // 3. Build EventOptions from identity + eventOptions.\n const eventOptions = buildEventOptions(\n identifyResolved,\n eventOptionsResolved,\n );\n const hasEventOptions = Object.keys(eventOptions).length > 0;\n\n // 4. Identify operations -- fires amplitude.identify() if op keys are present.\n if (identifyResolved) {\n const id = buildIdentify(amp, identifyResolved);\n if (id) amp.identify(id, hasEventOptions ? eventOptions : undefined);\n }\n\n // 5. Group assignment\n if (mappingSettings.group !== undefined) {\n const resolved = await getMappingValue(event, mappingSettings.group, {\n collector,\n });\n if (isObject(resolved)) {\n const { type, name } = resolved as {\n type?: unknown;\n name?: unknown;\n };\n if (isString(type) && (isString(name) || isArray(name))) {\n amp.setGroup(\n type,\n name as string | string[],\n hasEventOptions ? eventOptions : undefined,\n );\n }\n }\n }\n\n // 6. Group identify (properties on the group)\n if (mappingSettings.groupIdentify !== undefined) {\n const resolved = await getMappingValue(\n event,\n mappingSettings.groupIdentify,\n { collector },\n );\n if (isObject(resolved)) {\n const { type, name } = resolved as {\n type?: unknown;\n name?: unknown;\n };\n if (isString(type) && isString(name)) {\n const id = buildIdentify(amp, resolved as Record<string, unknown>);\n if (id)\n amp.groupIdentify(\n type,\n name,\n id,\n hasEventOptions ? eventOptions : undefined,\n );\n }\n }\n }\n\n // 7. Revenue -- single object or array from loop -> N revenue() calls.\n if (mappingSettings.revenue !== undefined) {\n const resolved = await getMappingValue(event, mappingSettings.revenue, {\n collector,\n });\n const items = isArray(resolved)\n ? (resolved as Record<string, unknown>[])\n : isObject(resolved)\n ? [resolved as Record<string, unknown>]\n : [];\n for (const item of items) {\n if (!isObject(item)) continue;\n amp.revenue(\n buildRevenue(amp, item),\n hasEventOptions ? eventOptions : undefined,\n );\n }\n }\n\n // 8. Default track (unless silent: true)\n if (rule?.silent !== true) {\n const eventType = isString(rule?.name) ? rule.name : event.name;\n\n // Resolve include: rule-level replaces destination-level.\n const include = mappingSettings.include ?? settings.include;\n const eventProperties: Record<string, unknown> = include\n ? flattenIncludeSections(event, include)\n : isObject(data)\n ? (data as Record<string, unknown>)\n : {};\n\n amp.track(\n eventType,\n eventProperties,\n hasEventOptions ? eventOptions : undefined,\n );\n }\n};\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationServer } from '@walkeros/server-core';\n\n/**\n * Amplitude SDK surface -- the subset of @amplitude/analytics-node the\n * destination actually uses. Tests provide a mock via env.amplitude;\n * production uses the real SDK import.\n *\n * Key difference from web: no setUserId/setDeviceId/setSessionId.\n * Identity is per-event via EventOptions on every SDK call.\n */\nexport interface AmplitudeSDK {\n init: (\n apiKey: string,\n options?: Record<string, unknown>,\n ) => { promise: Promise<void> };\n track: (\n eventType: string,\n eventProperties?: Record<string, unknown>,\n eventOptions?: EventOptions,\n ) => void;\n identify: (identify: IdentifyInstance, eventOptions?: EventOptions) => void;\n revenue: (revenue: RevenueInstance, eventOptions?: EventOptions) => void;\n setGroup: (\n groupType: string,\n groupName: string | string[],\n eventOptions?: EventOptions,\n ) => void;\n groupIdentify: (\n groupType: string,\n groupName: string,\n identify: IdentifyInstance,\n eventOptions?: EventOptions,\n ) => void;\n setOptOut: (optOut: boolean) => void;\n flush: () => { promise: Promise<void> };\n Identify: new () => IdentifyInstance;\n Revenue: new () => RevenueInstance;\n}\n\n/**\n * Per-event identity and metadata fields. Passed to every SDK call.\n * Maps directly to Amplitude's EventOptions type from the Node SDK.\n */\nexport interface EventOptions {\n user_id?: string;\n device_id?: string;\n session_id?: number;\n time?: number;\n insert_id?: string;\n ip?: string;\n city?: string;\n country?: string;\n region?: string;\n language?: string;\n platform?: string;\n os_name?: string;\n os_version?: string;\n device_brand?: string;\n device_manufacturer?: string;\n device_model?: string;\n app_version?: string;\n carrier?: string;\n user_agent?: string;\n groups?: Record<string, unknown>;\n plan?: Record<string, unknown>;\n ingestion_metadata?: Record<string, unknown>;\n partner_id?: string;\n extra?: Record<string, unknown>;\n}\n\n/** Chainable Identify -- mirrors @amplitude/analytics-node Identify class. */\nexport interface IdentifyInstance {\n set: (property: string, value: unknown) => IdentifyInstance;\n setOnce: (property: string, value: unknown) => IdentifyInstance;\n add: (property: string, value: number) => IdentifyInstance;\n append: (property: string, value: unknown) => IdentifyInstance;\n prepend: (property: string, value: unknown) => IdentifyInstance;\n preInsert: (property: string, value: unknown) => IdentifyInstance;\n postInsert: (property: string, value: unknown) => IdentifyInstance;\n remove: (property: string, value: unknown) => IdentifyInstance;\n unset: (property: string) => IdentifyInstance;\n clearAll: () => IdentifyInstance;\n}\n\n/** Chainable Revenue -- mirrors @amplitude/analytics-node Revenue class. */\nexport interface RevenueInstance {\n setProductId: (id: string) => RevenueInstance;\n setPrice: (price: number) => RevenueInstance;\n setQuantity: (quantity: number) => RevenueInstance;\n setRevenueType: (type: string) => RevenueInstance;\n setCurrency: (currency: string) => RevenueInstance;\n setRevenue: (revenue: number) => RevenueInstance;\n setReceipt: (receipt: string) => RevenueInstance;\n setReceiptSig: (signature: string) => RevenueInstance;\n setEventProperties: (properties: Record<string, unknown>) => RevenueInstance;\n}\n\n/**\n * Destination-level settings. apiKey is required; all other fields\n * are optional and passed through to the Amplitude Node SDK init().\n */\nexport interface Settings {\n apiKey: string;\n serverZone?: 'US' | 'EU';\n flushIntervalMillis?: number;\n flushQueueSize?: number;\n flushMaxRetries?: number;\n useBatch?: boolean;\n minIdLength?: number;\n serverUrl?: string;\n logLevel?: number;\n optOut?: boolean;\n offline?: boolean;\n enableRequestBodyCompression?: boolean;\n plan?: Record<string, unknown>;\n ingestionMetadata?: Record<string, unknown>;\n\n /** walkerOS mapping value for per-event identity resolution. */\n identify?: WalkerOSMapping.Value;\n /** walkerOS mapping value for per-event EventOptions (time, insert_id, ip, etc). */\n eventOptions?: WalkerOSMapping.Value;\n /** Sections to include as event_properties (e.g., ['data', 'globals']). */\n include?: string[];\n}\n\nexport type InitSettings = Partial<Settings>;\n\n/**\n * Per-rule mapping settings. Each is a walkerOS mapping value resolved\n * via getMappingValue(). Revenue supports loop for multi-item orders.\n */\nexport interface Mapping {\n identify?: WalkerOSMapping.Value;\n revenue?: WalkerOSMapping.Value;\n group?: WalkerOSMapping.Value;\n groupIdentify?: WalkerOSMapping.Value;\n eventOptions?: WalkerOSMapping.Value;\n include?: string[];\n}\n\n/**\n * Env -- optional SDK override. Production leaves this undefined and the\n * destination falls back to the real @amplitude/analytics-node import.\n * Tests provide a mock via env.amplitude.\n */\nexport interface Env extends DestinationServer.Env {\n amplitude?: AmplitudeSDK;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport interface Destination extends DestinationServer.Destination<Types> {\n init: DestinationServer.InitFn<Types>;\n}\n\nexport type Config = {\n settings: Settings;\n} & DestinationServer.Config<Types>;\n\nexport type InitFn = DestinationServer.InitFn<Types>;\nexport type PushFn = DestinationServer.PushFn<Types>;\n\nexport type PartialConfig = DestinationServer.PartialConfig<Types>;\n\nexport type PushEvents = DestinationServer.PushEvents<Mapping>;\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/push.ts","../src/transport.ts","../src/types/index.ts"],"sourcesContent":["import type { WalkerOS } from '@walkeros/core';\nimport type { Destination, Env } from './types';\nimport { retryBudget } from '@walkeros/server-core';\nimport { getConfig } from './config';\nimport { push, pushBatch, getAmplitude } from './push';\nimport { AmplitudeTransport } from './transport';\n\nexport * as DestinationAmplitude from './types';\n\n/**\n * The SDK's longest total wait before `retries` retries: the first waits\n * `flushIntervalMillis` (here the budget's wait), each later one a second\n * longer than the one before (1 s, 2 s, ...).\n */\nfunction amplitudeWaits(retries: number, wait: number): number {\n return retries && wait + 500 * retries * (retries - 1);\n}\n\nexport const destinationAmplitude: Destination = {\n type: 'amplitude',\n\n // Batched by default, at the SDK's own former defaults (200 events, 10 s).\n config: { batch: { size: 200, wait: 10_000 } },\n\n async init({ config: partialConfig, env }) {\n const config = getConfig(partialConfig);\n const amp = getAmplitude(env);\n\n // Destructure walkerOS-specific keys; the rest flow through to the SDK.\n const {\n apiKey,\n identify: _identify,\n eventOptions: _eventOptions,\n include: _include,\n ...nodeOptions\n } = config.settings;\n\n // Each push and each batch flushes the SDK and waits until Amplitude\n // answered its calls. The SDK's own retry ends within config.timeout: a\n // transport whose requests end after a quarter of it, and as many\n // retries as fit with the SDK's waits. A value set in the settings wins.\n const budget = retryBudget(partialConfig.timeout, amplitudeWaits);\n await amp.init(apiKey, {\n transportProvider: new AmplitudeTransport(budget.attempt),\n flushMaxRetries: budget.retries + 1,\n flushIntervalMillis: budget.wait,\n ...nodeOptions,\n }).promise;\n return config;\n },\n\n push,\n\n pushBatch,\n\n on(type, context) {\n if (type !== 'consent' || !context.data) return;\n const amp = getAmplitude(context.env);\n\n const consent = context.data as WalkerOS.Consent;\n const required = context.config?.consent;\n if (!required || Object.keys(required).length === 0) return;\n\n const allGranted = Object.keys(required).every(\n (key) => consent[key] === true,\n );\n amp.setOptOut(!allGranted);\n },\n\n async destroy({ env }) {\n const amp = getAmplitude(env);\n await amp.flush().promise;\n },\n};\n\nexport default destinationAmplitude;\n","import type { Config, Settings, PartialConfig } from './types';\nimport { StepError } from '@walkeros/core';\n\nexport function getConfig(partialConfig: PartialConfig = {}): Config {\n const settings: Partial<Settings> = partialConfig.settings || {};\n const { apiKey } = settings;\n\n if (!apiKey)\n throw new StepError('Config settings apiKey missing', { code: 'config' });\n\n const settingsConfig: Settings = {\n ...settings,\n apiKey,\n };\n\n return { ...partialConfig, settings: settingsConfig };\n}\n","import type { Collector, Destination, Logger, WalkerOS } from '@walkeros/core';\nimport type {\n PushFn,\n PushBatchFn,\n AmplitudeSDK,\n EventOptions,\n IdentifyInstance,\n Rule,\n Settings,\n} from './types';\nimport {\n getMappingValue,\n isArray,\n isDefined,\n isNumber,\n isObject,\n isString,\n isThenable,\n flattenIncludeSections,\n StepError,\n} from '@walkeros/core';\nimport { batchOutcome } from '@walkeros/server-core';\nimport * as amplitudeSDK from '@amplitude/analytics-node';\n\ntype IdentifyOpKey =\n | 'set'\n | 'setOnce'\n | 'add'\n | 'append'\n | 'prepend'\n | 'preInsert'\n | 'postInsert'\n | 'remove';\n\nconst IDENTIFY_OP_KEYS: IdentifyOpKey[] = [\n 'set',\n 'setOnce',\n 'add',\n 'append',\n 'prepend',\n 'preInsert',\n 'postInsert',\n 'remove',\n];\n\n/** Identity keys that map to EventOptions fields (not Identify operations). */\nconst IDENTITY_KEYS = ['user_id', 'device_id', 'session_id'] as const;\n\ntype RevenueFieldSetter =\n | 'setProductId'\n | 'setPrice'\n | 'setQuantity'\n | 'setRevenueType'\n | 'setCurrency'\n | 'setRevenue'\n | 'setReceipt'\n | 'setReceiptSig';\n\nconst REVENUE_FIELD_MAP: Record<string, RevenueFieldSetter> = {\n productId: 'setProductId',\n price: 'setPrice',\n quantity: 'setQuantity',\n revenueType: 'setRevenueType',\n currency: 'setCurrency',\n revenue: 'setRevenue',\n receipt: 'setReceipt',\n receiptSig: 'setReceiptSig',\n};\n\n/** An injected client (tests, simulation): one that can at least track. */\nfunction isAmplitudeSDK(value: unknown): value is AmplitudeSDK {\n return isObject(value) && typeof value.track === 'function';\n}\n\n/** The client of `env.amplitude` when one is injected, else the SDK. */\nexport function getAmplitude(env: unknown): AmplitudeSDK {\n const injected = isObject(env) ? env.amplitude : undefined;\n return isAmplitudeSDK(injected)\n ? injected\n : (amplitudeSDK as unknown as AmplitudeSDK);\n}\n\n/**\n * Hand one call to the SDK; the promise settles once Amplitude answered it.\n * The SDK resolves the call's promise after the send with a result whose\n * `code` is the HTTP status (0 for a failed request), never rejecting, so a\n * code outside 2xx fails. An injected client may answer with a promise of its\n * own.\n */\nasync function call(message: string, send: () => unknown): Promise<void> {\n let result: unknown;\n try {\n const answer: unknown = await send();\n result =\n isObject(answer) && isThenable(answer.promise)\n ? await answer.promise\n : answer;\n } catch (error) {\n throw new StepError(message, { code: 'transport', cause: error });\n }\n const code =\n isObject(result) && isNumber(result.code) ? result.code : undefined;\n if (code !== undefined && (code < 200 || code >= 300))\n throw new StepError(`${message} (code ${code})`, {\n code: 'transport',\n status: code || undefined,\n });\n}\n\n/**\n * Deterministic string -> positive integer hash (djb2).\n * Used for session_id when walkerOS provides a string session.\n */\nfunction hashStringToInt(value: string): number {\n let hash = 5381;\n for (let i = 0; i < value.length; i++) {\n hash = ((hash << 5) + hash + value.charCodeAt(i)) | 0;\n }\n return Math.abs(hash);\n}\n\nfunction buildIdentify(\n amp: AmplitudeSDK,\n resolved: Record<string, unknown>,\n): IdentifyInstance | undefined {\n let identify: IdentifyInstance | undefined;\n const ensure = () => {\n if (!identify) identify = new amp.Identify();\n return identify;\n };\n\n for (const op of IDENTIFY_OP_KEYS) {\n const bag = resolved[op];\n if (!isObject(bag)) continue;\n for (const [prop, value] of Object.entries(bag)) {\n if (!isDefined(value)) continue;\n if (op === 'add') {\n if (isNumber(value)) ensure().add(prop, value);\n } else {\n ensure()[op](prop, value);\n }\n }\n }\n\n if (isArray(resolved.unset)) {\n for (const prop of resolved.unset) {\n if (isString(prop)) ensure().unset(prop);\n }\n }\n\n if (resolved.clearAll === true) {\n ensure().clearAll();\n }\n\n return identify;\n}\n\nfunction buildRevenue(\n amp: AmplitudeSDK,\n item: Record<string, unknown>,\n): InstanceType<AmplitudeSDK['Revenue']> {\n const rev = new amp.Revenue();\n for (const [key, setter] of Object.entries(REVENUE_FIELD_MAP)) {\n const value = item[key];\n if (!isDefined(value)) continue;\n (rev[setter] as (v: unknown) => unknown)(value);\n }\n if (isObject(item.eventProperties)) {\n rev.setEventProperties(item.eventProperties as Record<string, unknown>);\n }\n return rev;\n}\n\n/**\n * Build EventOptions from resolved identity and eventOptions mappings.\n */\nfunction buildEventOptions(\n identityResolved?: Record<string, unknown>,\n eventOptionsResolved?: Record<string, unknown>,\n): EventOptions {\n const opts: EventOptions = {};\n\n if (identityResolved) {\n if (isString(identityResolved.user_id))\n opts.user_id = identityResolved.user_id;\n if (isString(identityResolved.device_id))\n opts.device_id = identityResolved.device_id;\n if (identityResolved.session_id !== undefined) {\n const session = identityResolved.session_id;\n opts.session_id = isNumber(session)\n ? session\n : isString(session) && /^\\d+$/.test(session)\n ? Number(session)\n : isString(session)\n ? hashStringToInt(session)\n : undefined;\n }\n }\n\n if (eventOptionsResolved) {\n // Copy all recognized EventOptions fields\n for (const [key, value] of Object.entries(eventOptionsResolved)) {\n if (\n isDefined(value) &&\n !IDENTITY_KEYS.includes(key as (typeof IDENTITY_KEYS)[number])\n ) {\n (opts as Record<string, unknown>)[key] = value;\n }\n }\n }\n\n return opts;\n}\n\n/**\n * A configured mapping that resolved to nothing skips its call: a declared\n * skip, said once at DEBUG with the event and the mapping key, never the\n * value, so a wrong mapping path does not look like an event without data.\n */\nfunction skipped(\n logger: Logger.Instance,\n event: WalkerOS.Event,\n call: string,\n key: string,\n): void {\n logger.debug(`Amplitude skipped ${call}: ${key} resolved to nothing`, {\n event: event.name,\n });\n}\n\n/** A call's outcome, marked as handled until its batch reads it. */\nfunction held(sent: Promise<void>): Promise<void> {\n sent.catch(() => undefined);\n return sent;\n}\n\n/** The settled calls of one event: the first failure, if any. */\nasync function delivered(calls: Promise<PromiseSettledResult<void>[]>) {\n for (const result of await calls)\n if (result.status === 'rejected') throw result.reason;\n}\n\n/**\n * Hand the calls of one event to the SDK (identify, group, group identify,\n * revenue, track); each returned promise settles on its answer.\n */\nasync function enqueue(\n amp: AmplitudeSDK,\n event: WalkerOS.Event,\n rule: Rule | undefined,\n data: Destination.Data,\n settings: Partial<Settings>,\n collector: Collector.Instance,\n logger: Logger.Instance,\n): Promise<Promise<void>[]> {\n const mappingSettings = rule?.settings || {};\n const calls: Promise<void>[] = [];\n\n // 1. Resolve identity -- rule-level override wins over destination-level.\n const identifyMapping = mappingSettings.identify ?? settings.identify;\n let identifyResolved: Record<string, unknown> | undefined;\n if (identifyMapping !== undefined) {\n const resolved = await getMappingValue(event, identifyMapping, {\n collector,\n });\n if (isObject(resolved)) {\n identifyResolved = resolved as Record<string, unknown>;\n } else skipped(logger, event, 'identify', 'identify');\n }\n\n // 2. Resolve eventOptions -- rule-level override wins over destination-level.\n const eventOptionsMapping =\n mappingSettings.eventOptions ?? settings.eventOptions;\n let eventOptionsResolved: Record<string, unknown> | undefined;\n if (eventOptionsMapping !== undefined) {\n const resolved = await getMappingValue(event, eventOptionsMapping, {\n collector,\n });\n if (isObject(resolved)) {\n eventOptionsResolved = resolved as Record<string, unknown>;\n }\n }\n\n // 3. Build EventOptions from identity + eventOptions.\n const eventOptions = buildEventOptions(\n identifyResolved,\n eventOptionsResolved,\n );\n const hasEventOptions = Object.keys(eventOptions).length > 0;\n\n // The id Amplitude deduplicates a call by (insert_id): the event id for\n // the track call, the event id and the call type for the others, so a\n // resent event is dropped and the calls of one event are not. A mapped\n // insert_id wins.\n const optionsFor = (type?: string): EventOptions | undefined => {\n if (!event.id) return hasEventOptions ? eventOptions : undefined;\n const insert_id = type ? `${event.id}-${type}` : event.id;\n return { insert_id, ...eventOptions };\n };\n\n // 4. Identify operations -- fires amplitude.identify() if op keys are present.\n if (identifyResolved) {\n const id = buildIdentify(amp, identifyResolved);\n if (id)\n calls.push(\n held(\n call('Amplitude identify failed', () =>\n amp.identify(id, optionsFor('identify')),\n ),\n ),\n );\n }\n\n // 5. Group assignment\n if (mappingSettings.group !== undefined) {\n const resolved = await getMappingValue(event, mappingSettings.group, {\n collector,\n });\n const type = isObject(resolved) ? resolved.type : undefined;\n const name = isObject(resolved) ? resolved.name : undefined;\n if (\n isString(type) &&\n (isString(name) || (isArray(name) && name.every(isString)))\n )\n calls.push(\n held(\n call('Amplitude setGroup failed', () =>\n amp.setGroup(type, name, optionsFor('group')),\n ),\n ),\n );\n else skipped(logger, event, 'group', 'group');\n }\n\n // 6. Group identify (properties on the group)\n if (mappingSettings.groupIdentify !== undefined) {\n const resolved = await getMappingValue(\n event,\n mappingSettings.groupIdentify,\n { collector },\n );\n const type = isObject(resolved) ? resolved.type : undefined;\n const name = isObject(resolved) ? resolved.name : undefined;\n const id =\n isObject(resolved) && isString(type) && isString(name)\n ? buildIdentify(amp, resolved)\n : undefined;\n if (id && isString(type) && isString(name))\n calls.push(\n held(\n call('Amplitude groupIdentify failed', () =>\n amp.groupIdentify(type, name, id, optionsFor('groupIdentify')),\n ),\n ),\n );\n else skipped(logger, event, 'groupIdentify', 'groupIdentify');\n }\n\n // 7. Revenue -- single object or array from loop -> N revenue() calls.\n if (mappingSettings.revenue !== undefined) {\n const resolved = await getMappingValue(event, mappingSettings.revenue, {\n collector,\n });\n const items = isArray(resolved)\n ? (resolved as Record<string, unknown>[])\n : isObject(resolved)\n ? [resolved as Record<string, unknown>]\n : [];\n if (!items.some(isObject)) skipped(logger, event, 'revenue', 'revenue');\n for (const [index, item] of items.entries()) {\n if (!isObject(item)) continue;\n calls.push(\n held(\n call('Amplitude revenue failed', () =>\n amp.revenue(\n buildRevenue(amp, item),\n optionsFor(`revenue-${index}`),\n ),\n ),\n ),\n );\n }\n }\n\n // 8. Default track (unless silent: true)\n if (rule?.silent !== true) {\n const eventType = isString(rule?.name) ? rule.name : event.name;\n\n // Resolve include: rule-level replaces destination-level.\n const include = mappingSettings.include ?? settings.include;\n const eventProperties: Record<string, unknown> = include\n ? flattenIncludeSections(event, include)\n : isObject(data)\n ? (data as Record<string, unknown>)\n : {};\n\n calls.push(\n held(\n call('Amplitude track failed', () =>\n amp.track(eventType, eventProperties, optionsFor()),\n ),\n ),\n );\n }\n\n return calls;\n}\n\n/** Ask the SDK to send what it holds. */\nasync function flush(amp: AmplitudeSDK): Promise<void> {\n await amp.flush().promise;\n}\n\n/** The settings init resolved, or a StepError. */\nfunction settingsOf(\n settings: Partial<Settings> | undefined,\n): Partial<Settings> {\n if (!settings)\n throw new StepError('Amplitude settings missing, init() not run', {\n code: 'not-ready',\n });\n return settings;\n}\n\n/** One event: its calls handed to the SDK, sent, and their answers awaited. */\nexport const push: PushFn = async function (\n event,\n { config, rule, data, collector, env, logger },\n) {\n const amp = getAmplitude(env);\n const settings = settingsOf(config.settings);\n const calls = await enqueue(\n amp,\n event,\n rule,\n data,\n settings,\n collector,\n logger,\n );\n if (calls.length) {\n const settled = Promise.allSettled(calls);\n await flush(amp);\n await delivered(settled);\n }\n};\n\n/**\n * A flushed `config.batch` batch: every entry's calls handed to the SDK, one\n * flush, and each entry's own outcome from its answers. When nothing landed,\n * the batch throws.\n */\nexport const pushBatch: PushBatchFn = async function (\n batch,\n { config, collector, env, logger },\n) {\n const amp = getAmplitude(env);\n const settings = settingsOf(config.settings);\n\n // Each entry's answers, or why its calls could not be handed over.\n const outcomes: Array<{\n settled?: Promise<PromiseSettledResult<void>[]>;\n error?: unknown;\n }> = [];\n for (const entry of batch.entries) {\n try {\n const calls = await enqueue(\n amp,\n entry.event,\n entry.rule,\n entry.data,\n settings,\n collector,\n logger,\n );\n outcomes.push({ settled: Promise.allSettled(calls) });\n } catch (error) {\n outcomes.push({ error });\n }\n }\n\n await flush(amp);\n\n const failed: Destination.BatchFailure[] = [];\n for (const [index, { settled, error }] of outcomes.entries()) {\n if (!settled) failed.push({ index, error });\n else\n await delivered(settled).catch((reason: unknown) =>\n failed.push({ index, error: reason }),\n );\n }\n\n return batchOutcome(batch.entries.length, failed);\n};\n","import type { Payload, Response, Transport } from '@amplitude/analytics-core';\nimport { BaseTransport, Status } from '@amplitude/analytics-core';\nimport { isObject, StepError } from '@walkeros/core';\nimport { sendServer } from '@walkeros/server-core';\n\n/**\n * The answers the SDK retries: a 5xx, a 408 and an unreadable answer. Under\n * an explicit flush the SDK retries only a send that throws, so these throw.\n */\nconst RETRIED: Status[] = [Status.Failed, Status.Timeout, Status.Unknown];\n\n/**\n * The SDK's transport, on the global `fetch` through `sendServer`: every\n * request ends after `timeout` ms (a quarter of `config.timeout`). The SDK's\n * own Node transport has none, so a request Amplitude never answered kept\n * its flush running and every later flush was skipped. A request that times\n * out or fails on the network throws, as does an answer the SDK retries: the\n * SDK then sends the events again, an explicit flush included, up to\n * `flushMaxRetries` attempts.\n */\nexport class AmplitudeTransport extends BaseTransport implements Transport {\n constructor(private readonly timeout: number) {\n super();\n }\n\n async send(serverUrl: string, payload: Payload): Promise<Response | null> {\n let status = 200;\n let body: unknown;\n try {\n ({ data: body } = await sendServer(serverUrl, JSON.stringify(payload), {\n timeout: this.timeout,\n }));\n } catch (error) {\n // An answer other than 2xx carries its status and body; anything else\n // (a timeout, a network failure) is a send that failed.\n if (!(error instanceof StepError) || error.status === undefined)\n throw error;\n status = error.status;\n body = error.body;\n }\n\n const response = this.buildResponse({\n code: status,\n ...(isObject(body) ? body : {}),\n });\n if (!response || RETRIED.includes(response.status))\n throw new StepError(`Amplitude answered ${status}`, {\n code: 'transport',\n status,\n });\n return response;\n }\n}\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationServer } from '@walkeros/server-core';\n\n/**\n * Amplitude SDK surface -- the subset of @amplitude/analytics-node the\n * destination actually uses. Tests provide a mock via env.amplitude;\n * production uses the real SDK import.\n *\n * Key difference from web: no setUserId/setDeviceId/setSessionId.\n * Identity is per-event via EventOptions on every SDK call.\n */\n/**\n * What an SDK call answers: a promise of its result, which resolves after\n * the event was sent (with its HTTP `code`) instead of rejecting.\n */\nexport interface AmplitudeReturn {\n promise: Promise<unknown>;\n}\n\nexport interface AmplitudeSDK {\n init: (\n apiKey: string,\n options?: Record<string, unknown>,\n ) => { promise: Promise<void> };\n track: (\n eventType: string,\n eventProperties?: Record<string, unknown>,\n eventOptions?: EventOptions,\n ) => AmplitudeReturn;\n identify: (\n identify: IdentifyInstance,\n eventOptions?: EventOptions,\n ) => AmplitudeReturn;\n revenue: (\n revenue: RevenueInstance,\n eventOptions?: EventOptions,\n ) => AmplitudeReturn;\n setGroup: (\n groupType: string,\n groupName: string | string[],\n eventOptions?: EventOptions,\n ) => AmplitudeReturn;\n groupIdentify: (\n groupType: string,\n groupName: string,\n identify: IdentifyInstance,\n eventOptions?: EventOptions,\n ) => AmplitudeReturn;\n setOptOut: (optOut: boolean) => void;\n flush: () => { promise: Promise<void> };\n Identify: new () => IdentifyInstance;\n Revenue: new () => RevenueInstance;\n}\n\n/**\n * Per-event identity and metadata fields. Passed to every SDK call.\n * Maps directly to Amplitude's EventOptions type from the Node SDK.\n */\nexport interface EventOptions {\n user_id?: string;\n device_id?: string;\n session_id?: number;\n time?: number;\n insert_id?: string;\n ip?: string;\n city?: string;\n country?: string;\n region?: string;\n language?: string;\n platform?: string;\n os_name?: string;\n os_version?: string;\n device_brand?: string;\n device_manufacturer?: string;\n device_model?: string;\n app_version?: string;\n carrier?: string;\n user_agent?: string;\n groups?: Record<string, unknown>;\n plan?: Record<string, unknown>;\n ingestion_metadata?: Record<string, unknown>;\n partner_id?: string;\n extra?: Record<string, unknown>;\n}\n\n/** Chainable Identify -- mirrors @amplitude/analytics-node Identify class. */\nexport interface IdentifyInstance {\n set: (property: string, value: unknown) => IdentifyInstance;\n setOnce: (property: string, value: unknown) => IdentifyInstance;\n add: (property: string, value: number) => IdentifyInstance;\n append: (property: string, value: unknown) => IdentifyInstance;\n prepend: (property: string, value: unknown) => IdentifyInstance;\n preInsert: (property: string, value: unknown) => IdentifyInstance;\n postInsert: (property: string, value: unknown) => IdentifyInstance;\n remove: (property: string, value: unknown) => IdentifyInstance;\n unset: (property: string) => IdentifyInstance;\n clearAll: () => IdentifyInstance;\n}\n\n/** Chainable Revenue -- mirrors @amplitude/analytics-node Revenue class. */\nexport interface RevenueInstance {\n setProductId: (id: string) => RevenueInstance;\n setPrice: (price: number) => RevenueInstance;\n setQuantity: (quantity: number) => RevenueInstance;\n setRevenueType: (type: string) => RevenueInstance;\n setCurrency: (currency: string) => RevenueInstance;\n setRevenue: (revenue: number) => RevenueInstance;\n setReceipt: (receipt: string) => RevenueInstance;\n setReceiptSig: (signature: string) => RevenueInstance;\n setEventProperties: (properties: Record<string, unknown>) => RevenueInstance;\n}\n\n/**\n * Destination-level settings. apiKey is required; all other fields\n * are optional and passed through to the Amplitude Node SDK init().\n */\nexport interface Settings {\n apiKey: string;\n serverZone?: 'US' | 'EU';\n flushIntervalMillis?: number;\n flushQueueSize?: number;\n flushMaxRetries?: number;\n useBatch?: boolean;\n minIdLength?: number;\n serverUrl?: string;\n logLevel?: number;\n optOut?: boolean;\n offline?: boolean;\n enableRequestBodyCompression?: boolean;\n plan?: Record<string, unknown>;\n ingestionMetadata?: Record<string, unknown>;\n\n /** walkerOS mapping value for per-event identity resolution. */\n identify?: WalkerOSMapping.Value;\n /** walkerOS mapping value for per-event EventOptions (time, insert_id, ip, etc). */\n eventOptions?: WalkerOSMapping.Value;\n /** Sections to include as event_properties (e.g., ['data', 'globals']). */\n include?: string[];\n}\n\nexport type InitSettings = Partial<Settings>;\n\n/**\n * Per-rule mapping settings. Each is a walkerOS mapping value resolved\n * via getMappingValue(). Revenue supports loop for multi-item orders.\n */\nexport interface Mapping {\n identify?: WalkerOSMapping.Value;\n revenue?: WalkerOSMapping.Value;\n group?: WalkerOSMapping.Value;\n groupIdentify?: WalkerOSMapping.Value;\n eventOptions?: WalkerOSMapping.Value;\n include?: string[];\n}\n\n/**\n * Env -- optional SDK override. Production leaves this undefined and the\n * destination falls back to the real @amplitude/analytics-node import.\n * Tests provide a mock via env.amplitude.\n */\nexport interface Env extends DestinationServer.Env {\n amplitude?: AmplitudeSDK;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport interface Destination extends DestinationServer.Destination<Types> {\n init: DestinationServer.InitFn<Types>;\n}\n\nexport type Config = {\n settings: Settings;\n} & DestinationServer.Config<Types>;\n\nexport type InitFn = DestinationServer.InitFn<Types>;\nexport type PushFn = DestinationServer.PushFn<Types>;\nexport type PushBatchFn = CoreDestination.PushBatchFn<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"],"mappings":"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","names":["import_server_core","import_core","call","import_core","import_server_core"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getMappingValue,isArray,isDefined,isNumber,isObject,isString,flattenIncludeSections}from"@walkeros/core";import*as amplitudeSDK from"@amplitude/analytics-node";var IDENTIFY_OP_KEYS=["set","setOnce","add","append","prepend","preInsert","postInsert","remove"],IDENTITY_KEYS=["user_id","device_id","session_id"],REVENUE_FIELD_MAP={productId:"setProductId",price:"setPrice",quantity:"setQuantity",revenueType:"setRevenueType",currency:"setCurrency",revenue:"setRevenue",receipt:"setReceipt",receiptSig:"setReceiptSig"};function getAmplitude(env){const
|
|
1
|
+
import{createRequire}from"module";createRequire(import.meta.url);import{retryBudget}from"@walkeros/server-core";import{StepError}from"@walkeros/core";import{getMappingValue,isArray,isDefined,isNumber,isObject,isString,isThenable,flattenIncludeSections,StepError as StepError2}from"@walkeros/core";import{batchOutcome}from"@walkeros/server-core";import*as amplitudeSDK from"@amplitude/analytics-node";var IDENTIFY_OP_KEYS=["set","setOnce","add","append","prepend","preInsert","postInsert","remove"],IDENTITY_KEYS=["user_id","device_id","session_id"],REVENUE_FIELD_MAP={productId:"setProductId",price:"setPrice",quantity:"setQuantity",revenueType:"setRevenueType",currency:"setCurrency",revenue:"setRevenue",receipt:"setReceipt",receiptSig:"setReceiptSig"};function getAmplitude(env){const injected=isObject(env)?env.amplitude:void 0;return isObject(value=injected)&&"function"==typeof value.track?injected:amplitudeSDK;var value}async function call(message,send){let result;try{const answer=await send();result=isObject(answer)&&isThenable(answer.promise)?await answer.promise:answer}catch(error){throw new StepError2(message,{code:"transport",cause:error})}const code=isObject(result)&&isNumber(result.code)?result.code:void 0;if(void 0!==code&&(code<200||code>=300))throw new StepError2(`${message} (code ${code})`,{code:"transport",status:code||void 0})}function buildIdentify(amp,resolved){let identify;const ensure=()=>(identify||(identify=new amp.Identify),identify);for(const op of IDENTIFY_OP_KEYS){const bag=resolved[op];if(isObject(bag))for(const[prop,value]of Object.entries(bag))isDefined(value)&&("add"===op?isNumber(value)&&ensure().add(prop,value):ensure()[op](prop,value))}if(isArray(resolved.unset))for(const prop of resolved.unset)isString(prop)&&ensure().unset(prop);return!0===resolved.clearAll&&ensure().clearAll(),identify}function buildRevenue(amp,item){const rev=new amp.Revenue;for(const[key,setter]of Object.entries(REVENUE_FIELD_MAP)){const value=item[key];isDefined(value)&&rev[setter](value)}return isObject(item.eventProperties)&&rev.setEventProperties(item.eventProperties),rev}function buildEventOptions(identityResolved,eventOptionsResolved){const opts={};if(identityResolved&&(isString(identityResolved.user_id)&&(opts.user_id=identityResolved.user_id),isString(identityResolved.device_id)&&(opts.device_id=identityResolved.device_id),void 0!==identityResolved.session_id)){const session=identityResolved.session_id;opts.session_id=isNumber(session)?session:isString(session)&&/^\d+$/.test(session)?Number(session):isString(session)?function(value){let hash=5381;for(let i=0;i<value.length;i++)hash=(hash<<5)+hash+value.charCodeAt(i)|0;return Math.abs(hash)}(session):void 0}if(eventOptionsResolved)for(const[key,value]of Object.entries(eventOptionsResolved))isDefined(value)&&!IDENTITY_KEYS.includes(key)&&(opts[key]=value);return opts}function skipped(logger,event,call2,key){logger.debug(`Amplitude skipped ${call2}: ${key} resolved to nothing`,{event:event.name})}function held(sent){return sent.catch(()=>{}),sent}async function delivered(calls){for(const result of await calls)if("rejected"===result.status)throw result.reason}async function enqueue(amp,event,rule,data,settings,collector,logger){const mappingSettings=rule?.settings||{},calls=[],identifyMapping=mappingSettings.identify??settings.identify;let identifyResolved;if(void 0!==identifyMapping){const resolved=await getMappingValue(event,identifyMapping,{collector:collector});isObject(resolved)?identifyResolved=resolved:skipped(logger,event,"identify","identify")}const eventOptionsMapping=mappingSettings.eventOptions??settings.eventOptions;let eventOptionsResolved;if(void 0!==eventOptionsMapping){const resolved=await getMappingValue(event,eventOptionsMapping,{collector:collector});isObject(resolved)&&(eventOptionsResolved=resolved)}const eventOptions=buildEventOptions(identifyResolved,eventOptionsResolved),hasEventOptions=Object.keys(eventOptions).length>0,optionsFor=type=>{if(!event.id)return hasEventOptions?eventOptions:void 0;return{insert_id:type?`${event.id}-${type}`:event.id,...eventOptions}};if(identifyResolved){const id=buildIdentify(amp,identifyResolved);id&&calls.push(held(call("Amplitude identify failed",()=>amp.identify(id,optionsFor("identify")))))}if(void 0!==mappingSettings.group){const resolved=await getMappingValue(event,mappingSettings.group,{collector:collector}),type=isObject(resolved)?resolved.type:void 0,name=isObject(resolved)?resolved.name:void 0;isString(type)&&(isString(name)||isArray(name)&&name.every(isString))?calls.push(held(call("Amplitude setGroup failed",()=>amp.setGroup(type,name,optionsFor("group"))))):skipped(logger,event,"group","group")}if(void 0!==mappingSettings.groupIdentify){const resolved=await getMappingValue(event,mappingSettings.groupIdentify,{collector:collector}),type=isObject(resolved)?resolved.type:void 0,name=isObject(resolved)?resolved.name:void 0,id=isObject(resolved)&&isString(type)&&isString(name)?buildIdentify(amp,resolved):void 0;id&&isString(type)&&isString(name)?calls.push(held(call("Amplitude groupIdentify failed",()=>amp.groupIdentify(type,name,id,optionsFor("groupIdentify"))))):skipped(logger,event,"groupIdentify","groupIdentify")}if(void 0!==mappingSettings.revenue){const resolved=await getMappingValue(event,mappingSettings.revenue,{collector:collector}),items=isArray(resolved)?resolved:isObject(resolved)?[resolved]:[];items.some(isObject)||skipped(logger,event,"revenue","revenue");for(const[index,item]of items.entries())isObject(item)&&calls.push(held(call("Amplitude revenue failed",()=>amp.revenue(buildRevenue(amp,item),optionsFor(`revenue-${index}`)))))}if(!0!==rule?.silent){const eventType=isString(rule?.name)?rule.name:event.name,include=mappingSettings.include??settings.include,eventProperties=include?flattenIncludeSections(event,include):isObject(data)?data:{};calls.push(held(call("Amplitude track failed",()=>amp.track(eventType,eventProperties,optionsFor()))))}return calls}async function flush(amp){await amp.flush().promise}function settingsOf(settings){if(!settings)throw new StepError2("Amplitude settings missing, init() not run",{code:"not-ready"});return settings}import{BaseTransport,Status}from"@amplitude/analytics-core";import{isObject as isObject2,StepError as StepError3}from"@walkeros/core";import{sendServer}from"@walkeros/server-core";var RETRIED=[Status.Failed,Status.Timeout,Status.Unknown],AmplitudeTransport=class extends BaseTransport{constructor(timeout){super(),this.timeout=timeout}timeout;async send(serverUrl,payload){let body,status=200;try{({data:body}=await sendServer(serverUrl,JSON.stringify(payload),{timeout:this.timeout}))}catch(error){if(!(error instanceof StepError3)||void 0===error.status)throw error;status=error.status,body=error.body}const response=this.buildResponse({code:status,...isObject2(body)?body:{}});if(!response||RETRIED.includes(response.status))throw new StepError3(`Amplitude answered ${status}`,{code:"transport",status:status});return response}},types_exports={};function amplitudeWaits(retries,wait){return retries&&wait+500*retries*(retries-1)}var destinationAmplitude={type:"amplitude",config:{batch:{size:200,wait:1e4}},async init({config:partialConfig,env:env}){const config=function(partialConfig={}){const settings=partialConfig.settings||{},{apiKey:apiKey}=settings;if(!apiKey)throw new StepError("Config settings apiKey missing",{code:"config"});const settingsConfig={...settings,apiKey:apiKey};return{...partialConfig,settings:settingsConfig}}(partialConfig),amp=getAmplitude(env),{apiKey:apiKey,identify:_identify,eventOptions:_eventOptions,include:_include,...nodeOptions}=config.settings,budget=retryBudget(partialConfig.timeout,amplitudeWaits);return await amp.init(apiKey,{transportProvider:new AmplitudeTransport(budget.attempt),flushMaxRetries:budget.retries+1,flushIntervalMillis:budget.wait,...nodeOptions}).promise,config},push:async function(event,{config:config,rule:rule,data:data,collector:collector,env:env,logger:logger}){const amp=getAmplitude(env),settings=settingsOf(config.settings),calls=await enqueue(amp,event,rule,data,settings,collector,logger);if(calls.length){const settled=Promise.allSettled(calls);await flush(amp),await delivered(settled)}},pushBatch:async function(batch,{config:config,collector:collector,env:env,logger:logger}){const amp=getAmplitude(env),settings=settingsOf(config.settings),outcomes=[];for(const entry of batch.entries)try{const calls=await enqueue(amp,entry.event,entry.rule,entry.data,settings,collector,logger);outcomes.push({settled:Promise.allSettled(calls)})}catch(error){outcomes.push({error:error})}await flush(amp);const failed=[];for(const[index,{settled:settled,error:error}]of outcomes.entries())settled?await delivered(settled).catch(reason=>failed.push({index:index,error:reason})):failed.push({index:index,error:error});return batchOutcome(batch.entries.length,failed)},on(type,context){if("consent"!==type||!context.data)return;const amp=getAmplitude(context.env),consent=context.data,required=context.config?.consent;if(!required||0===Object.keys(required).length)return;const allGranted=Object.keys(required).every(key=>!0===consent[key]);amp.setOptOut(!allGranted)},async destroy({env:env}){const amp=getAmplitude(env);await amp.flush().promise}},index_default=destinationAmplitude;export{types_exports as DestinationAmplitude,index_default as default,destinationAmplitude};//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config.ts","../src/push.ts","../src/types/index.ts","../src/index.ts"],"sourcesContent":["import type { Config, Settings, PartialConfig } from './types';\nimport type { Logger } from '@walkeros/core';\n\nexport function getConfig(\n partialConfig: PartialConfig = {},\n logger: Logger.Instance,\n): Config {\n const settings = (partialConfig.settings || {}) as Partial<Settings>;\n const { apiKey } = settings;\n\n if (!apiKey) logger.throw('Config settings apiKey missing');\n\n const settingsConfig: Settings = {\n ...settings,\n apiKey,\n };\n\n return { ...partialConfig, settings: settingsConfig };\n}\n","import type {\n PushFn,\n AmplitudeSDK,\n EventOptions,\n IdentifyInstance,\n} from './types';\nimport {\n getMappingValue,\n isArray,\n isDefined,\n isNumber,\n isObject,\n isString,\n flattenIncludeSections,\n} from '@walkeros/core';\nimport * as amplitudeSDK from '@amplitude/analytics-node';\n\ntype IdentifyOpKey =\n | 'set'\n | 'setOnce'\n | 'add'\n | 'append'\n | 'prepend'\n | 'preInsert'\n | 'postInsert'\n | 'remove';\n\nconst IDENTIFY_OP_KEYS: IdentifyOpKey[] = [\n 'set',\n 'setOnce',\n 'add',\n 'append',\n 'prepend',\n 'preInsert',\n 'postInsert',\n 'remove',\n];\n\n/** Identity keys that map to EventOptions fields (not Identify operations). */\nconst IDENTITY_KEYS = ['user_id', 'device_id', 'session_id'] as const;\n\ntype RevenueFieldSetter =\n | 'setProductId'\n | 'setPrice'\n | 'setQuantity'\n | 'setRevenueType'\n | 'setCurrency'\n | 'setRevenue'\n | 'setReceipt'\n | 'setReceiptSig';\n\nconst REVENUE_FIELD_MAP: Record<string, RevenueFieldSetter> = {\n productId: 'setProductId',\n price: 'setPrice',\n quantity: 'setQuantity',\n revenueType: 'setRevenueType',\n currency: 'setCurrency',\n revenue: 'setRevenue',\n receipt: 'setReceipt',\n receiptSig: 'setReceiptSig',\n};\n\nexport function getAmplitude(\n env: Record<string, unknown> | undefined,\n): AmplitudeSDK {\n const amplitude = env?.amplitude as AmplitudeSDK | undefined;\n return amplitude ?? (amplitudeSDK as unknown as AmplitudeSDK);\n}\n\n/**\n * Deterministic string -> positive integer hash (djb2).\n * Used for session_id when walkerOS provides a string session.\n */\nfunction hashStringToInt(value: string): number {\n let hash = 5381;\n for (let i = 0; i < value.length; i++) {\n hash = ((hash << 5) + hash + value.charCodeAt(i)) | 0;\n }\n return Math.abs(hash);\n}\n\nfunction buildIdentify(\n amp: AmplitudeSDK,\n resolved: Record<string, unknown>,\n): IdentifyInstance | undefined {\n let identify: IdentifyInstance | undefined;\n const ensure = () => {\n if (!identify) identify = new amp.Identify();\n return identify;\n };\n\n for (const op of IDENTIFY_OP_KEYS) {\n const bag = resolved[op];\n if (!isObject(bag)) continue;\n for (const [prop, value] of Object.entries(bag)) {\n if (!isDefined(value)) continue;\n if (op === 'add') {\n if (isNumber(value)) ensure().add(prop, value);\n } else {\n ensure()[op](prop, value);\n }\n }\n }\n\n if (isArray(resolved.unset)) {\n for (const prop of resolved.unset) {\n if (isString(prop)) ensure().unset(prop);\n }\n }\n\n if (resolved.clearAll === true) {\n ensure().clearAll();\n }\n\n return identify;\n}\n\nfunction buildRevenue(\n amp: AmplitudeSDK,\n item: Record<string, unknown>,\n): InstanceType<AmplitudeSDK['Revenue']> {\n const rev = new amp.Revenue();\n for (const [key, setter] of Object.entries(REVENUE_FIELD_MAP)) {\n const value = item[key];\n if (!isDefined(value)) continue;\n (rev[setter] as (v: unknown) => unknown)(value);\n }\n if (isObject(item.eventProperties)) {\n rev.setEventProperties(item.eventProperties as Record<string, unknown>);\n }\n return rev;\n}\n\n/**\n * Build EventOptions from resolved identity and eventOptions mappings.\n */\nfunction buildEventOptions(\n identityResolved?: Record<string, unknown>,\n eventOptionsResolved?: Record<string, unknown>,\n): EventOptions {\n const opts: EventOptions = {};\n\n if (identityResolved) {\n if (isString(identityResolved.user_id))\n opts.user_id = identityResolved.user_id;\n if (isString(identityResolved.device_id))\n opts.device_id = identityResolved.device_id;\n if (identityResolved.session_id !== undefined) {\n const session = identityResolved.session_id;\n opts.session_id = isNumber(session)\n ? session\n : isString(session) && /^\\d+$/.test(session)\n ? Number(session)\n : isString(session)\n ? hashStringToInt(session)\n : undefined;\n }\n }\n\n if (eventOptionsResolved) {\n // Copy all recognized EventOptions fields\n for (const [key, value] of Object.entries(eventOptionsResolved)) {\n if (\n isDefined(value) &&\n !IDENTITY_KEYS.includes(key as (typeof IDENTITY_KEYS)[number])\n ) {\n (opts as Record<string, unknown>)[key] = value;\n }\n }\n }\n\n return opts;\n}\n\nexport const push: PushFn = async function (\n event,\n { config, rule, data, collector, env },\n) {\n const amp = getAmplitude(env as Record<string, unknown> | undefined);\n const settings = config.settings!;\n const mappingSettings = rule?.settings || {};\n\n // 1. Resolve identity -- rule-level override wins over destination-level.\n const identifyMapping = mappingSettings.identify ?? settings.identify;\n let identifyResolved: Record<string, unknown> | undefined;\n if (identifyMapping !== undefined) {\n const resolved = await getMappingValue(event, identifyMapping, {\n collector,\n });\n if (isObject(resolved)) {\n identifyResolved = resolved as Record<string, unknown>;\n }\n }\n\n // 2. Resolve eventOptions -- rule-level override wins over destination-level.\n const eventOptionsMapping =\n mappingSettings.eventOptions ?? settings.eventOptions;\n let eventOptionsResolved: Record<string, unknown> | undefined;\n if (eventOptionsMapping !== undefined) {\n const resolved = await getMappingValue(event, eventOptionsMapping, {\n collector,\n });\n if (isObject(resolved)) {\n eventOptionsResolved = resolved as Record<string, unknown>;\n }\n }\n\n // 3. Build EventOptions from identity + eventOptions.\n const eventOptions = buildEventOptions(\n identifyResolved,\n eventOptionsResolved,\n );\n const hasEventOptions = Object.keys(eventOptions).length > 0;\n\n // 4. Identify operations -- fires amplitude.identify() if op keys are present.\n if (identifyResolved) {\n const id = buildIdentify(amp, identifyResolved);\n if (id) amp.identify(id, hasEventOptions ? eventOptions : undefined);\n }\n\n // 5. Group assignment\n if (mappingSettings.group !== undefined) {\n const resolved = await getMappingValue(event, mappingSettings.group, {\n collector,\n });\n if (isObject(resolved)) {\n const { type, name } = resolved as {\n type?: unknown;\n name?: unknown;\n };\n if (isString(type) && (isString(name) || isArray(name))) {\n amp.setGroup(\n type,\n name as string | string[],\n hasEventOptions ? eventOptions : undefined,\n );\n }\n }\n }\n\n // 6. Group identify (properties on the group)\n if (mappingSettings.groupIdentify !== undefined) {\n const resolved = await getMappingValue(\n event,\n mappingSettings.groupIdentify,\n { collector },\n );\n if (isObject(resolved)) {\n const { type, name } = resolved as {\n type?: unknown;\n name?: unknown;\n };\n if (isString(type) && isString(name)) {\n const id = buildIdentify(amp, resolved as Record<string, unknown>);\n if (id)\n amp.groupIdentify(\n type,\n name,\n id,\n hasEventOptions ? eventOptions : undefined,\n );\n }\n }\n }\n\n // 7. Revenue -- single object or array from loop -> N revenue() calls.\n if (mappingSettings.revenue !== undefined) {\n const resolved = await getMappingValue(event, mappingSettings.revenue, {\n collector,\n });\n const items = isArray(resolved)\n ? (resolved as Record<string, unknown>[])\n : isObject(resolved)\n ? [resolved as Record<string, unknown>]\n : [];\n for (const item of items) {\n if (!isObject(item)) continue;\n amp.revenue(\n buildRevenue(amp, item),\n hasEventOptions ? eventOptions : undefined,\n );\n }\n }\n\n // 8. Default track (unless silent: true)\n if (rule?.silent !== true) {\n const eventType = isString(rule?.name) ? rule.name : event.name;\n\n // Resolve include: rule-level replaces destination-level.\n const include = mappingSettings.include ?? settings.include;\n const eventProperties: Record<string, unknown> = include\n ? flattenIncludeSections(event, include)\n : isObject(data)\n ? (data as Record<string, unknown>)\n : {};\n\n amp.track(\n eventType,\n eventProperties,\n hasEventOptions ? eventOptions : undefined,\n );\n }\n};\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationServer } from '@walkeros/server-core';\n\n/**\n * Amplitude SDK surface -- the subset of @amplitude/analytics-node the\n * destination actually uses. Tests provide a mock via env.amplitude;\n * production uses the real SDK import.\n *\n * Key difference from web: no setUserId/setDeviceId/setSessionId.\n * Identity is per-event via EventOptions on every SDK call.\n */\nexport interface AmplitudeSDK {\n init: (\n apiKey: string,\n options?: Record<string, unknown>,\n ) => { promise: Promise<void> };\n track: (\n eventType: string,\n eventProperties?: Record<string, unknown>,\n eventOptions?: EventOptions,\n ) => void;\n identify: (identify: IdentifyInstance, eventOptions?: EventOptions) => void;\n revenue: (revenue: RevenueInstance, eventOptions?: EventOptions) => void;\n setGroup: (\n groupType: string,\n groupName: string | string[],\n eventOptions?: EventOptions,\n ) => void;\n groupIdentify: (\n groupType: string,\n groupName: string,\n identify: IdentifyInstance,\n eventOptions?: EventOptions,\n ) => void;\n setOptOut: (optOut: boolean) => void;\n flush: () => { promise: Promise<void> };\n Identify: new () => IdentifyInstance;\n Revenue: new () => RevenueInstance;\n}\n\n/**\n * Per-event identity and metadata fields. Passed to every SDK call.\n * Maps directly to Amplitude's EventOptions type from the Node SDK.\n */\nexport interface EventOptions {\n user_id?: string;\n device_id?: string;\n session_id?: number;\n time?: number;\n insert_id?: string;\n ip?: string;\n city?: string;\n country?: string;\n region?: string;\n language?: string;\n platform?: string;\n os_name?: string;\n os_version?: string;\n device_brand?: string;\n device_manufacturer?: string;\n device_model?: string;\n app_version?: string;\n carrier?: string;\n user_agent?: string;\n groups?: Record<string, unknown>;\n plan?: Record<string, unknown>;\n ingestion_metadata?: Record<string, unknown>;\n partner_id?: string;\n extra?: Record<string, unknown>;\n}\n\n/** Chainable Identify -- mirrors @amplitude/analytics-node Identify class. */\nexport interface IdentifyInstance {\n set: (property: string, value: unknown) => IdentifyInstance;\n setOnce: (property: string, value: unknown) => IdentifyInstance;\n add: (property: string, value: number) => IdentifyInstance;\n append: (property: string, value: unknown) => IdentifyInstance;\n prepend: (property: string, value: unknown) => IdentifyInstance;\n preInsert: (property: string, value: unknown) => IdentifyInstance;\n postInsert: (property: string, value: unknown) => IdentifyInstance;\n remove: (property: string, value: unknown) => IdentifyInstance;\n unset: (property: string) => IdentifyInstance;\n clearAll: () => IdentifyInstance;\n}\n\n/** Chainable Revenue -- mirrors @amplitude/analytics-node Revenue class. */\nexport interface RevenueInstance {\n setProductId: (id: string) => RevenueInstance;\n setPrice: (price: number) => RevenueInstance;\n setQuantity: (quantity: number) => RevenueInstance;\n setRevenueType: (type: string) => RevenueInstance;\n setCurrency: (currency: string) => RevenueInstance;\n setRevenue: (revenue: number) => RevenueInstance;\n setReceipt: (receipt: string) => RevenueInstance;\n setReceiptSig: (signature: string) => RevenueInstance;\n setEventProperties: (properties: Record<string, unknown>) => RevenueInstance;\n}\n\n/**\n * Destination-level settings. apiKey is required; all other fields\n * are optional and passed through to the Amplitude Node SDK init().\n */\nexport interface Settings {\n apiKey: string;\n serverZone?: 'US' | 'EU';\n flushIntervalMillis?: number;\n flushQueueSize?: number;\n flushMaxRetries?: number;\n useBatch?: boolean;\n minIdLength?: number;\n serverUrl?: string;\n logLevel?: number;\n optOut?: boolean;\n offline?: boolean;\n enableRequestBodyCompression?: boolean;\n plan?: Record<string, unknown>;\n ingestionMetadata?: Record<string, unknown>;\n\n /** walkerOS mapping value for per-event identity resolution. */\n identify?: WalkerOSMapping.Value;\n /** walkerOS mapping value for per-event EventOptions (time, insert_id, ip, etc). */\n eventOptions?: WalkerOSMapping.Value;\n /** Sections to include as event_properties (e.g., ['data', 'globals']). */\n include?: string[];\n}\n\nexport type InitSettings = Partial<Settings>;\n\n/**\n * Per-rule mapping settings. Each is a walkerOS mapping value resolved\n * via getMappingValue(). Revenue supports loop for multi-item orders.\n */\nexport interface Mapping {\n identify?: WalkerOSMapping.Value;\n revenue?: WalkerOSMapping.Value;\n group?: WalkerOSMapping.Value;\n groupIdentify?: WalkerOSMapping.Value;\n eventOptions?: WalkerOSMapping.Value;\n include?: string[];\n}\n\n/**\n * Env -- optional SDK override. Production leaves this undefined and the\n * destination falls back to the real @amplitude/analytics-node import.\n * Tests provide a mock via env.amplitude.\n */\nexport interface Env extends DestinationServer.Env {\n amplitude?: AmplitudeSDK;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport interface Destination extends DestinationServer.Destination<Types> {\n init: DestinationServer.InitFn<Types>;\n}\n\nexport type Config = {\n settings: Settings;\n} & DestinationServer.Config<Types>;\n\nexport type InitFn = DestinationServer.InitFn<Types>;\nexport type PushFn = DestinationServer.PushFn<Types>;\n\nexport type PartialConfig = DestinationServer.PartialConfig<Types>;\n\nexport type PushEvents = DestinationServer.PushEvents<Mapping>;\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n","import type { WalkerOS } from '@walkeros/core';\nimport type { Destination, Env } from './types';\nimport { getConfig } from './config';\nimport { push, getAmplitude } from './push';\n\nexport * as DestinationAmplitude from './types';\n\nexport const destinationAmplitude: Destination = {\n type: 'amplitude',\n\n config: {},\n\n async init({ config: partialConfig, env, logger }) {\n const config = getConfig(partialConfig, logger);\n const amp = getAmplitude(env as Record<string, unknown> | undefined);\n\n // Destructure walkerOS-specific keys; the rest flow through to the SDK.\n const {\n apiKey,\n identify: _identify,\n eventOptions: _eventOptions,\n include: _include,\n ...nodeOptions\n } = config.settings;\n\n await amp.init(apiKey, nodeOptions).promise;\n return config;\n },\n\n async push(event, context) {\n return await push(event, context);\n },\n\n on(type, context) {\n if (type !== 'consent' || !context.data) return;\n const amp = getAmplitude(\n context.env as Record<string, unknown> | undefined,\n );\n\n const consent = context.data as WalkerOS.Consent;\n const required = context.config?.consent;\n if (!required || Object.keys(required).length === 0) return;\n\n const allGranted = Object.keys(required).every(\n (key) => consent[key] === true,\n );\n amp.setOptOut(!allGranted);\n },\n\n async destroy({ env }) {\n const amp = getAmplitude(env as Record<string, unknown> | undefined);\n await amp.flush().promise;\n },\n};\n\nexport default destinationAmplitude;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/push.ts","../src/transport.ts","../src/types/index.ts"],"sourcesContent":["import type { WalkerOS } from '@walkeros/core';\nimport type { Destination, Env } from './types';\nimport { retryBudget } from '@walkeros/server-core';\nimport { getConfig } from './config';\nimport { push, pushBatch, getAmplitude } from './push';\nimport { AmplitudeTransport } from './transport';\n\nexport * as DestinationAmplitude from './types';\n\n/**\n * The SDK's longest total wait before `retries` retries: the first waits\n * `flushIntervalMillis` (here the budget's wait), each later one a second\n * longer than the one before (1 s, 2 s, ...).\n */\nfunction amplitudeWaits(retries: number, wait: number): number {\n return retries && wait + 500 * retries * (retries - 1);\n}\n\nexport const destinationAmplitude: Destination = {\n type: 'amplitude',\n\n // Batched by default, at the SDK's own former defaults (200 events, 10 s).\n config: { batch: { size: 200, wait: 10_000 } },\n\n async init({ config: partialConfig, env }) {\n const config = getConfig(partialConfig);\n const amp = getAmplitude(env);\n\n // Destructure walkerOS-specific keys; the rest flow through to the SDK.\n const {\n apiKey,\n identify: _identify,\n eventOptions: _eventOptions,\n include: _include,\n ...nodeOptions\n } = config.settings;\n\n // Each push and each batch flushes the SDK and waits until Amplitude\n // answered its calls. The SDK's own retry ends within config.timeout: a\n // transport whose requests end after a quarter of it, and as many\n // retries as fit with the SDK's waits. A value set in the settings wins.\n const budget = retryBudget(partialConfig.timeout, amplitudeWaits);\n await amp.init(apiKey, {\n transportProvider: new AmplitudeTransport(budget.attempt),\n flushMaxRetries: budget.retries + 1,\n flushIntervalMillis: budget.wait,\n ...nodeOptions,\n }).promise;\n return config;\n },\n\n push,\n\n pushBatch,\n\n on(type, context) {\n if (type !== 'consent' || !context.data) return;\n const amp = getAmplitude(context.env);\n\n const consent = context.data as WalkerOS.Consent;\n const required = context.config?.consent;\n if (!required || Object.keys(required).length === 0) return;\n\n const allGranted = Object.keys(required).every(\n (key) => consent[key] === true,\n );\n amp.setOptOut(!allGranted);\n },\n\n async destroy({ env }) {\n const amp = getAmplitude(env);\n await amp.flush().promise;\n },\n};\n\nexport default destinationAmplitude;\n","import type { Config, Settings, PartialConfig } from './types';\nimport { StepError } from '@walkeros/core';\n\nexport function getConfig(partialConfig: PartialConfig = {}): Config {\n const settings: Partial<Settings> = partialConfig.settings || {};\n const { apiKey } = settings;\n\n if (!apiKey)\n throw new StepError('Config settings apiKey missing', { code: 'config' });\n\n const settingsConfig: Settings = {\n ...settings,\n apiKey,\n };\n\n return { ...partialConfig, settings: settingsConfig };\n}\n","import type { Collector, Destination, Logger, WalkerOS } from '@walkeros/core';\nimport type {\n PushFn,\n PushBatchFn,\n AmplitudeSDK,\n EventOptions,\n IdentifyInstance,\n Rule,\n Settings,\n} from './types';\nimport {\n getMappingValue,\n isArray,\n isDefined,\n isNumber,\n isObject,\n isString,\n isThenable,\n flattenIncludeSections,\n StepError,\n} from '@walkeros/core';\nimport { batchOutcome } from '@walkeros/server-core';\nimport * as amplitudeSDK from '@amplitude/analytics-node';\n\ntype IdentifyOpKey =\n | 'set'\n | 'setOnce'\n | 'add'\n | 'append'\n | 'prepend'\n | 'preInsert'\n | 'postInsert'\n | 'remove';\n\nconst IDENTIFY_OP_KEYS: IdentifyOpKey[] = [\n 'set',\n 'setOnce',\n 'add',\n 'append',\n 'prepend',\n 'preInsert',\n 'postInsert',\n 'remove',\n];\n\n/** Identity keys that map to EventOptions fields (not Identify operations). */\nconst IDENTITY_KEYS = ['user_id', 'device_id', 'session_id'] as const;\n\ntype RevenueFieldSetter =\n | 'setProductId'\n | 'setPrice'\n | 'setQuantity'\n | 'setRevenueType'\n | 'setCurrency'\n | 'setRevenue'\n | 'setReceipt'\n | 'setReceiptSig';\n\nconst REVENUE_FIELD_MAP: Record<string, RevenueFieldSetter> = {\n productId: 'setProductId',\n price: 'setPrice',\n quantity: 'setQuantity',\n revenueType: 'setRevenueType',\n currency: 'setCurrency',\n revenue: 'setRevenue',\n receipt: 'setReceipt',\n receiptSig: 'setReceiptSig',\n};\n\n/** An injected client (tests, simulation): one that can at least track. */\nfunction isAmplitudeSDK(value: unknown): value is AmplitudeSDK {\n return isObject(value) && typeof value.track === 'function';\n}\n\n/** The client of `env.amplitude` when one is injected, else the SDK. */\nexport function getAmplitude(env: unknown): AmplitudeSDK {\n const injected = isObject(env) ? env.amplitude : undefined;\n return isAmplitudeSDK(injected)\n ? injected\n : (amplitudeSDK as unknown as AmplitudeSDK);\n}\n\n/**\n * Hand one call to the SDK; the promise settles once Amplitude answered it.\n * The SDK resolves the call's promise after the send with a result whose\n * `code` is the HTTP status (0 for a failed request), never rejecting, so a\n * code outside 2xx fails. An injected client may answer with a promise of its\n * own.\n */\nasync function call(message: string, send: () => unknown): Promise<void> {\n let result: unknown;\n try {\n const answer: unknown = await send();\n result =\n isObject(answer) && isThenable(answer.promise)\n ? await answer.promise\n : answer;\n } catch (error) {\n throw new StepError(message, { code: 'transport', cause: error });\n }\n const code =\n isObject(result) && isNumber(result.code) ? result.code : undefined;\n if (code !== undefined && (code < 200 || code >= 300))\n throw new StepError(`${message} (code ${code})`, {\n code: 'transport',\n status: code || undefined,\n });\n}\n\n/**\n * Deterministic string -> positive integer hash (djb2).\n * Used for session_id when walkerOS provides a string session.\n */\nfunction hashStringToInt(value: string): number {\n let hash = 5381;\n for (let i = 0; i < value.length; i++) {\n hash = ((hash << 5) + hash + value.charCodeAt(i)) | 0;\n }\n return Math.abs(hash);\n}\n\nfunction buildIdentify(\n amp: AmplitudeSDK,\n resolved: Record<string, unknown>,\n): IdentifyInstance | undefined {\n let identify: IdentifyInstance | undefined;\n const ensure = () => {\n if (!identify) identify = new amp.Identify();\n return identify;\n };\n\n for (const op of IDENTIFY_OP_KEYS) {\n const bag = resolved[op];\n if (!isObject(bag)) continue;\n for (const [prop, value] of Object.entries(bag)) {\n if (!isDefined(value)) continue;\n if (op === 'add') {\n if (isNumber(value)) ensure().add(prop, value);\n } else {\n ensure()[op](prop, value);\n }\n }\n }\n\n if (isArray(resolved.unset)) {\n for (const prop of resolved.unset) {\n if (isString(prop)) ensure().unset(prop);\n }\n }\n\n if (resolved.clearAll === true) {\n ensure().clearAll();\n }\n\n return identify;\n}\n\nfunction buildRevenue(\n amp: AmplitudeSDK,\n item: Record<string, unknown>,\n): InstanceType<AmplitudeSDK['Revenue']> {\n const rev = new amp.Revenue();\n for (const [key, setter] of Object.entries(REVENUE_FIELD_MAP)) {\n const value = item[key];\n if (!isDefined(value)) continue;\n (rev[setter] as (v: unknown) => unknown)(value);\n }\n if (isObject(item.eventProperties)) {\n rev.setEventProperties(item.eventProperties as Record<string, unknown>);\n }\n return rev;\n}\n\n/**\n * Build EventOptions from resolved identity and eventOptions mappings.\n */\nfunction buildEventOptions(\n identityResolved?: Record<string, unknown>,\n eventOptionsResolved?: Record<string, unknown>,\n): EventOptions {\n const opts: EventOptions = {};\n\n if (identityResolved) {\n if (isString(identityResolved.user_id))\n opts.user_id = identityResolved.user_id;\n if (isString(identityResolved.device_id))\n opts.device_id = identityResolved.device_id;\n if (identityResolved.session_id !== undefined) {\n const session = identityResolved.session_id;\n opts.session_id = isNumber(session)\n ? session\n : isString(session) && /^\\d+$/.test(session)\n ? Number(session)\n : isString(session)\n ? hashStringToInt(session)\n : undefined;\n }\n }\n\n if (eventOptionsResolved) {\n // Copy all recognized EventOptions fields\n for (const [key, value] of Object.entries(eventOptionsResolved)) {\n if (\n isDefined(value) &&\n !IDENTITY_KEYS.includes(key as (typeof IDENTITY_KEYS)[number])\n ) {\n (opts as Record<string, unknown>)[key] = value;\n }\n }\n }\n\n return opts;\n}\n\n/**\n * A configured mapping that resolved to nothing skips its call: a declared\n * skip, said once at DEBUG with the event and the mapping key, never the\n * value, so a wrong mapping path does not look like an event without data.\n */\nfunction skipped(\n logger: Logger.Instance,\n event: WalkerOS.Event,\n call: string,\n key: string,\n): void {\n logger.debug(`Amplitude skipped ${call}: ${key} resolved to nothing`, {\n event: event.name,\n });\n}\n\n/** A call's outcome, marked as handled until its batch reads it. */\nfunction held(sent: Promise<void>): Promise<void> {\n sent.catch(() => undefined);\n return sent;\n}\n\n/** The settled calls of one event: the first failure, if any. */\nasync function delivered(calls: Promise<PromiseSettledResult<void>[]>) {\n for (const result of await calls)\n if (result.status === 'rejected') throw result.reason;\n}\n\n/**\n * Hand the calls of one event to the SDK (identify, group, group identify,\n * revenue, track); each returned promise settles on its answer.\n */\nasync function enqueue(\n amp: AmplitudeSDK,\n event: WalkerOS.Event,\n rule: Rule | undefined,\n data: Destination.Data,\n settings: Partial<Settings>,\n collector: Collector.Instance,\n logger: Logger.Instance,\n): Promise<Promise<void>[]> {\n const mappingSettings = rule?.settings || {};\n const calls: Promise<void>[] = [];\n\n // 1. Resolve identity -- rule-level override wins over destination-level.\n const identifyMapping = mappingSettings.identify ?? settings.identify;\n let identifyResolved: Record<string, unknown> | undefined;\n if (identifyMapping !== undefined) {\n const resolved = await getMappingValue(event, identifyMapping, {\n collector,\n });\n if (isObject(resolved)) {\n identifyResolved = resolved as Record<string, unknown>;\n } else skipped(logger, event, 'identify', 'identify');\n }\n\n // 2. Resolve eventOptions -- rule-level override wins over destination-level.\n const eventOptionsMapping =\n mappingSettings.eventOptions ?? settings.eventOptions;\n let eventOptionsResolved: Record<string, unknown> | undefined;\n if (eventOptionsMapping !== undefined) {\n const resolved = await getMappingValue(event, eventOptionsMapping, {\n collector,\n });\n if (isObject(resolved)) {\n eventOptionsResolved = resolved as Record<string, unknown>;\n }\n }\n\n // 3. Build EventOptions from identity + eventOptions.\n const eventOptions = buildEventOptions(\n identifyResolved,\n eventOptionsResolved,\n );\n const hasEventOptions = Object.keys(eventOptions).length > 0;\n\n // The id Amplitude deduplicates a call by (insert_id): the event id for\n // the track call, the event id and the call type for the others, so a\n // resent event is dropped and the calls of one event are not. A mapped\n // insert_id wins.\n const optionsFor = (type?: string): EventOptions | undefined => {\n if (!event.id) return hasEventOptions ? eventOptions : undefined;\n const insert_id = type ? `${event.id}-${type}` : event.id;\n return { insert_id, ...eventOptions };\n };\n\n // 4. Identify operations -- fires amplitude.identify() if op keys are present.\n if (identifyResolved) {\n const id = buildIdentify(amp, identifyResolved);\n if (id)\n calls.push(\n held(\n call('Amplitude identify failed', () =>\n amp.identify(id, optionsFor('identify')),\n ),\n ),\n );\n }\n\n // 5. Group assignment\n if (mappingSettings.group !== undefined) {\n const resolved = await getMappingValue(event, mappingSettings.group, {\n collector,\n });\n const type = isObject(resolved) ? resolved.type : undefined;\n const name = isObject(resolved) ? resolved.name : undefined;\n if (\n isString(type) &&\n (isString(name) || (isArray(name) && name.every(isString)))\n )\n calls.push(\n held(\n call('Amplitude setGroup failed', () =>\n amp.setGroup(type, name, optionsFor('group')),\n ),\n ),\n );\n else skipped(logger, event, 'group', 'group');\n }\n\n // 6. Group identify (properties on the group)\n if (mappingSettings.groupIdentify !== undefined) {\n const resolved = await getMappingValue(\n event,\n mappingSettings.groupIdentify,\n { collector },\n );\n const type = isObject(resolved) ? resolved.type : undefined;\n const name = isObject(resolved) ? resolved.name : undefined;\n const id =\n isObject(resolved) && isString(type) && isString(name)\n ? buildIdentify(amp, resolved)\n : undefined;\n if (id && isString(type) && isString(name))\n calls.push(\n held(\n call('Amplitude groupIdentify failed', () =>\n amp.groupIdentify(type, name, id, optionsFor('groupIdentify')),\n ),\n ),\n );\n else skipped(logger, event, 'groupIdentify', 'groupIdentify');\n }\n\n // 7. Revenue -- single object or array from loop -> N revenue() calls.\n if (mappingSettings.revenue !== undefined) {\n const resolved = await getMappingValue(event, mappingSettings.revenue, {\n collector,\n });\n const items = isArray(resolved)\n ? (resolved as Record<string, unknown>[])\n : isObject(resolved)\n ? [resolved as Record<string, unknown>]\n : [];\n if (!items.some(isObject)) skipped(logger, event, 'revenue', 'revenue');\n for (const [index, item] of items.entries()) {\n if (!isObject(item)) continue;\n calls.push(\n held(\n call('Amplitude revenue failed', () =>\n amp.revenue(\n buildRevenue(amp, item),\n optionsFor(`revenue-${index}`),\n ),\n ),\n ),\n );\n }\n }\n\n // 8. Default track (unless silent: true)\n if (rule?.silent !== true) {\n const eventType = isString(rule?.name) ? rule.name : event.name;\n\n // Resolve include: rule-level replaces destination-level.\n const include = mappingSettings.include ?? settings.include;\n const eventProperties: Record<string, unknown> = include\n ? flattenIncludeSections(event, include)\n : isObject(data)\n ? (data as Record<string, unknown>)\n : {};\n\n calls.push(\n held(\n call('Amplitude track failed', () =>\n amp.track(eventType, eventProperties, optionsFor()),\n ),\n ),\n );\n }\n\n return calls;\n}\n\n/** Ask the SDK to send what it holds. */\nasync function flush(amp: AmplitudeSDK): Promise<void> {\n await amp.flush().promise;\n}\n\n/** The settings init resolved, or a StepError. */\nfunction settingsOf(\n settings: Partial<Settings> | undefined,\n): Partial<Settings> {\n if (!settings)\n throw new StepError('Amplitude settings missing, init() not run', {\n code: 'not-ready',\n });\n return settings;\n}\n\n/** One event: its calls handed to the SDK, sent, and their answers awaited. */\nexport const push: PushFn = async function (\n event,\n { config, rule, data, collector, env, logger },\n) {\n const amp = getAmplitude(env);\n const settings = settingsOf(config.settings);\n const calls = await enqueue(\n amp,\n event,\n rule,\n data,\n settings,\n collector,\n logger,\n );\n if (calls.length) {\n const settled = Promise.allSettled(calls);\n await flush(amp);\n await delivered(settled);\n }\n};\n\n/**\n * A flushed `config.batch` batch: every entry's calls handed to the SDK, one\n * flush, and each entry's own outcome from its answers. When nothing landed,\n * the batch throws.\n */\nexport const pushBatch: PushBatchFn = async function (\n batch,\n { config, collector, env, logger },\n) {\n const amp = getAmplitude(env);\n const settings = settingsOf(config.settings);\n\n // Each entry's answers, or why its calls could not be handed over.\n const outcomes: Array<{\n settled?: Promise<PromiseSettledResult<void>[]>;\n error?: unknown;\n }> = [];\n for (const entry of batch.entries) {\n try {\n const calls = await enqueue(\n amp,\n entry.event,\n entry.rule,\n entry.data,\n settings,\n collector,\n logger,\n );\n outcomes.push({ settled: Promise.allSettled(calls) });\n } catch (error) {\n outcomes.push({ error });\n }\n }\n\n await flush(amp);\n\n const failed: Destination.BatchFailure[] = [];\n for (const [index, { settled, error }] of outcomes.entries()) {\n if (!settled) failed.push({ index, error });\n else\n await delivered(settled).catch((reason: unknown) =>\n failed.push({ index, error: reason }),\n );\n }\n\n return batchOutcome(batch.entries.length, failed);\n};\n","import type { Payload, Response, Transport } from '@amplitude/analytics-core';\nimport { BaseTransport, Status } from '@amplitude/analytics-core';\nimport { isObject, StepError } from '@walkeros/core';\nimport { sendServer } from '@walkeros/server-core';\n\n/**\n * The answers the SDK retries: a 5xx, a 408 and an unreadable answer. Under\n * an explicit flush the SDK retries only a send that throws, so these throw.\n */\nconst RETRIED: Status[] = [Status.Failed, Status.Timeout, Status.Unknown];\n\n/**\n * The SDK's transport, on the global `fetch` through `sendServer`: every\n * request ends after `timeout` ms (a quarter of `config.timeout`). The SDK's\n * own Node transport has none, so a request Amplitude never answered kept\n * its flush running and every later flush was skipped. A request that times\n * out or fails on the network throws, as does an answer the SDK retries: the\n * SDK then sends the events again, an explicit flush included, up to\n * `flushMaxRetries` attempts.\n */\nexport class AmplitudeTransport extends BaseTransport implements Transport {\n constructor(private readonly timeout: number) {\n super();\n }\n\n async send(serverUrl: string, payload: Payload): Promise<Response | null> {\n let status = 200;\n let body: unknown;\n try {\n ({ data: body } = await sendServer(serverUrl, JSON.stringify(payload), {\n timeout: this.timeout,\n }));\n } catch (error) {\n // An answer other than 2xx carries its status and body; anything else\n // (a timeout, a network failure) is a send that failed.\n if (!(error instanceof StepError) || error.status === undefined)\n throw error;\n status = error.status;\n body = error.body;\n }\n\n const response = this.buildResponse({\n code: status,\n ...(isObject(body) ? body : {}),\n });\n if (!response || RETRIED.includes(response.status))\n throw new StepError(`Amplitude answered ${status}`, {\n code: 'transport',\n status,\n });\n return response;\n }\n}\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationServer } from '@walkeros/server-core';\n\n/**\n * Amplitude SDK surface -- the subset of @amplitude/analytics-node the\n * destination actually uses. Tests provide a mock via env.amplitude;\n * production uses the real SDK import.\n *\n * Key difference from web: no setUserId/setDeviceId/setSessionId.\n * Identity is per-event via EventOptions on every SDK call.\n */\n/**\n * What an SDK call answers: a promise of its result, which resolves after\n * the event was sent (with its HTTP `code`) instead of rejecting.\n */\nexport interface AmplitudeReturn {\n promise: Promise<unknown>;\n}\n\nexport interface AmplitudeSDK {\n init: (\n apiKey: string,\n options?: Record<string, unknown>,\n ) => { promise: Promise<void> };\n track: (\n eventType: string,\n eventProperties?: Record<string, unknown>,\n eventOptions?: EventOptions,\n ) => AmplitudeReturn;\n identify: (\n identify: IdentifyInstance,\n eventOptions?: EventOptions,\n ) => AmplitudeReturn;\n revenue: (\n revenue: RevenueInstance,\n eventOptions?: EventOptions,\n ) => AmplitudeReturn;\n setGroup: (\n groupType: string,\n groupName: string | string[],\n eventOptions?: EventOptions,\n ) => AmplitudeReturn;\n groupIdentify: (\n groupType: string,\n groupName: string,\n identify: IdentifyInstance,\n eventOptions?: EventOptions,\n ) => AmplitudeReturn;\n setOptOut: (optOut: boolean) => void;\n flush: () => { promise: Promise<void> };\n Identify: new () => IdentifyInstance;\n Revenue: new () => RevenueInstance;\n}\n\n/**\n * Per-event identity and metadata fields. Passed to every SDK call.\n * Maps directly to Amplitude's EventOptions type from the Node SDK.\n */\nexport interface EventOptions {\n user_id?: string;\n device_id?: string;\n session_id?: number;\n time?: number;\n insert_id?: string;\n ip?: string;\n city?: string;\n country?: string;\n region?: string;\n language?: string;\n platform?: string;\n os_name?: string;\n os_version?: string;\n device_brand?: string;\n device_manufacturer?: string;\n device_model?: string;\n app_version?: string;\n carrier?: string;\n user_agent?: string;\n groups?: Record<string, unknown>;\n plan?: Record<string, unknown>;\n ingestion_metadata?: Record<string, unknown>;\n partner_id?: string;\n extra?: Record<string, unknown>;\n}\n\n/** Chainable Identify -- mirrors @amplitude/analytics-node Identify class. */\nexport interface IdentifyInstance {\n set: (property: string, value: unknown) => IdentifyInstance;\n setOnce: (property: string, value: unknown) => IdentifyInstance;\n add: (property: string, value: number) => IdentifyInstance;\n append: (property: string, value: unknown) => IdentifyInstance;\n prepend: (property: string, value: unknown) => IdentifyInstance;\n preInsert: (property: string, value: unknown) => IdentifyInstance;\n postInsert: (property: string, value: unknown) => IdentifyInstance;\n remove: (property: string, value: unknown) => IdentifyInstance;\n unset: (property: string) => IdentifyInstance;\n clearAll: () => IdentifyInstance;\n}\n\n/** Chainable Revenue -- mirrors @amplitude/analytics-node Revenue class. */\nexport interface RevenueInstance {\n setProductId: (id: string) => RevenueInstance;\n setPrice: (price: number) => RevenueInstance;\n setQuantity: (quantity: number) => RevenueInstance;\n setRevenueType: (type: string) => RevenueInstance;\n setCurrency: (currency: string) => RevenueInstance;\n setRevenue: (revenue: number) => RevenueInstance;\n setReceipt: (receipt: string) => RevenueInstance;\n setReceiptSig: (signature: string) => RevenueInstance;\n setEventProperties: (properties: Record<string, unknown>) => RevenueInstance;\n}\n\n/**\n * Destination-level settings. apiKey is required; all other fields\n * are optional and passed through to the Amplitude Node SDK init().\n */\nexport interface Settings {\n apiKey: string;\n serverZone?: 'US' | 'EU';\n flushIntervalMillis?: number;\n flushQueueSize?: number;\n flushMaxRetries?: number;\n useBatch?: boolean;\n minIdLength?: number;\n serverUrl?: string;\n logLevel?: number;\n optOut?: boolean;\n offline?: boolean;\n enableRequestBodyCompression?: boolean;\n plan?: Record<string, unknown>;\n ingestionMetadata?: Record<string, unknown>;\n\n /** walkerOS mapping value for per-event identity resolution. */\n identify?: WalkerOSMapping.Value;\n /** walkerOS mapping value for per-event EventOptions (time, insert_id, ip, etc). */\n eventOptions?: WalkerOSMapping.Value;\n /** Sections to include as event_properties (e.g., ['data', 'globals']). */\n include?: string[];\n}\n\nexport type InitSettings = Partial<Settings>;\n\n/**\n * Per-rule mapping settings. Each is a walkerOS mapping value resolved\n * via getMappingValue(). Revenue supports loop for multi-item orders.\n */\nexport interface Mapping {\n identify?: WalkerOSMapping.Value;\n revenue?: WalkerOSMapping.Value;\n group?: WalkerOSMapping.Value;\n groupIdentify?: WalkerOSMapping.Value;\n eventOptions?: WalkerOSMapping.Value;\n include?: string[];\n}\n\n/**\n * Env -- optional SDK override. Production leaves this undefined and the\n * destination falls back to the real @amplitude/analytics-node import.\n * Tests provide a mock via env.amplitude.\n */\nexport interface Env extends DestinationServer.Env {\n amplitude?: AmplitudeSDK;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport interface Destination extends DestinationServer.Destination<Types> {\n init: DestinationServer.InitFn<Types>;\n}\n\nexport type Config = {\n settings: Settings;\n} & DestinationServer.Config<Types>;\n\nexport type InitFn = DestinationServer.InitFn<Types>;\nexport type PushFn = DestinationServer.PushFn<Types>;\nexport type PushBatchFn = CoreDestination.PushBatchFn<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"],"mappings":"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","names":["StepError","call","isObject","StepError"]}
|