@walkeros/web-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 +40 -0
- package/README.md +7 -0
- package/dist/dev.d.mts +56 -46
- package/dist/dev.d.ts +56 -46
- 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 +56 -46
- package/dist/examples/index.d.ts +56 -46
- package/dist/examples/index.js +31 -13
- package/dist/examples/index.mjs +32 -14
- package/dist/index.browser.js +1 -1
- package/dist/index.d.mts +46 -46
- package/dist/index.d.ts +46 -46
- package/dist/index.es5.js +1 -1
- 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 +133 -37
- package/package.json +7 -4
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e,t=Object.create,
|
|
1
|
+
"use strict";var e,t=Object.create,i=Object.defineProperty,n=Object.getOwnPropertyDescriptor,s=Object.getOwnPropertyNames,r=Object.getPrototypeOf,o=Object.prototype.hasOwnProperty,a=(e,t,r,a)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let c of s(t))o.call(e,c)||c===r||i(e,c,{get:()=>t[c],enumerable:!(a=n(t,c))||a.enumerable});return e},c={};((e,t)=>{for(var n in t)i(e,n,{get:t[n],enumerable:!0})})(c,{DestinationAmplitude:()=>l,default:()=>h,destinationAmplitude:()=>A}),module.exports=(e=c,a(i({},"__esModule",{value:!0}),e));var u,d=require("@walkeros/core"),p=((e,n,s)=>(s=null!=e?t(r(e)):{},a(!n&&e&&e.__esModule?s:i(s,"default",{value:e,enumerable:!0}),e)))(require("@amplitude/unified")),l={};function f(){if(u)return u;const e=p.createInstance();return u={init:(t,i)=>e.init(t,void 0,i),add:t=>e.add(t),track:(t,i)=>e.track(t,i),identify:t=>e.identify(t),revenue:t=>e.revenue(t),reset:()=>e.reset(),setOptOut:t=>e.setOptOut(t),setUserId:t=>e.setUserId(t),setDeviceId:t=>e.setDeviceId(t),setSessionId:t=>e.setSessionId(t),setGroup:(t,i)=>e.setGroup(t,i),groupIdentify:(t,i,n)=>e.groupIdentify(t,i,n),Identify:p.Identify,Revenue:p.Revenue}}function y(e){const t=(0,d.isObject)(e)?e.amplitude:void 0;if(void 0===t||(i=t,(0,d.isObject)(i)&&["init","add","track","identify","revenue","reset","setOptOut","setUserId","setDeviceId","setSessionId","setGroup","groupIdentify","Identify","Revenue"].every(e=>"function"==typeof i[e])))return t;var i;throw new d.StepError("env.amplitude is not an Amplitude SDK",{code:"config"})}var g=e=>(0,d.stepCall)(e,"transport","Amplitude call failed");function v(e,t,i,n){e.debug(`Amplitude skipped ${i}: ${n} resolved to nothing`,{event:t.name})}async function m(e){const t=await e();(0,d.isObject)(t)&&t.promise instanceof Promise&&await t.promise}async function w(e,t,i,n={}){const s={...n};if("user"in i){const r=i.user;null===r||""===r?(await t(()=>e.setUserId(void 0)),delete s.user):(0,d.isString)(r)&&r!==n.user&&(await t(()=>e.setUserId(r)),s.user=r)}if("device"in i){const r=i.device;(0,d.isString)(r)&&r!==n.device&&(await t(()=>e.setDeviceId(r)),s.device=r)}if("session"in i){const r=i.session,o=(0,d.isNumber)(r)?r:(0,d.isString)(r)&&/^\d+$/.test(r)?Number(r):(0,d.isString)(r)?function(e){let t=5381;for(let i=0;i<e.length;i++)t=(t<<5)+t+e.charCodeAt(i)|0;return Math.abs(t)}(r):void 0;void 0!==o&&o!==n.session&&(await t(()=>e.setSessionId(o)),s.session=o)}return s}var b=["set","setOnce","add","append","prepend","preInsert","postInsert","remove"];function I(e,t){let i;const n=()=>(i||(i=new e.Identify),i);for(const e of b){const i=t[e];if((0,d.isObject)(i))for(const[t,s]of Object.entries(i))(0,d.isDefined)(s)&&("add"===e?(0,d.isNumber)(s)&&n().add(t,s):n()[e](t,s))}if((0,d.isArray)(t.unset))for(const e of t.unset)(0,d.isString)(e)&&n().unset(e);return!0===t.clearAll&&n().clearAll(),i}function O(e,t){const i=new e.Revenue,{productId:n,price:s,quantity:r,revenueType:o,currency:a,revenue:c}=t,{receipt:u,receiptSig:p,eventProperties:l}=t;return(0,d.isString)(n)&&i.setProductId(n),(0,d.isNumber)(s)&&i.setPrice(s),(0,d.isNumber)(r)&&i.setQuantity(r),(0,d.isString)(o)&&i.setRevenueType(o),(0,d.isString)(a)&&i.setCurrency(a),(0,d.isNumber)(c)&&i.setRevenue(c),(0,d.isString)(u)&&i.setReceipt(u),(0,d.isString)(p)&&i.setReceiptSig(p),(0,d.isObject)(l)&&i.setEventProperties(l),i}async function S(e,t){const{apiKey:i,identify:n,sessionReplay:s,experiment:r,engagement:o,_state:a,...c}=t,u={autocapture:!1,identityStorage:"none",...c};await(0,d.stepCall)(()=>m(()=>e.init(i,u)),"vendor-not-ready","Amplitude init failed");const{serverZone:p,instanceName:l}=u,f={...p?{serverZone:p}:{},...l?{instanceName:l}:{}},y=[];s&&y.push(["Session Replay",async()=>{const{sessionReplayPlugin:e}=await import("@amplitude/plugin-session-replay-browser");return e({...s,...f})}]),(null==r?void 0:r.deploymentKey)&&y.push(["Experiment",async()=>{const{experimentPlugin:e}=await import("@amplitude/plugin-experiment-browser");return e({...r,...f})}]),o&&y.push(["Guides & Surveys",async()=>{const{plugin:e}=await import("@amplitude/engagement-browser");return e({...!0===o?{}:o,...f})}]);for(const[t,i]of y)await(0,d.stepCall)(async()=>{const t=await i();await m(()=>e.add(t))},"vendor-not-ready",`Amplitude ${t} failed to start`)}async function j(e,t,i,n,s,r,o,a){var c;const u=i._state||{};if(void 0!==n.reset){("boolean"==typeof n.reset?n.reset:await(0,d.getMappingValue)(t,n.reset,{collector:o}))&&(await g(()=>e.reset()),u.lastIdentity={})}const p=null!=(c=n.identify)?c:i.identify;let l;if(void 0!==p){const i=await(0,d.getMappingValue)(t,p,{collector:o});(0,d.isObject)(i)?(l=i,u.lastIdentity=await w(e,g,i,u.lastIdentity)):v(a,t,"identify","identify")}if(l){const t=I(e,l);t&&await g(()=>e.identify(t))}if(void 0!==n.group){const i=await(0,d.getMappingValue)(t,n.group,{collector:o}),{type:s,name:r}=(0,d.isObject)(i)?i:{};(0,d.isString)(s)&&((0,d.isString)(r)||(f=r,(0,d.isArray)(f)&&f.every(d.isString)))?await g(()=>e.setGroup(s,r)):v(a,t,"setGroup","group")}var f;if(void 0!==n.groupIdentify){const i=await(0,d.getMappingValue)(t,n.groupIdentify,{collector:o}),{type:s,name:r}=(0,d.isObject)(i)?i:{},c=(0,d.isObject)(i)&&(0,d.isString)(s)&&(0,d.isString)(r)?I(e,i):void 0;c&&(0,d.isString)(s)&&(0,d.isString)(r)?await g(()=>e.groupIdentify(s,r,c)):v(a,t,"groupIdentify","groupIdentify")}if(void 0!==n.revenue){const i=await(0,d.getMappingValue)(t,n.revenue,{collector:o});let s=0;for(const t of(0,d.isArray)(i)?i:[i]){if(!(0,d.isObject)(t))continue;const i=O(e,t);await g(()=>e.revenue(i)),s++}s||v(a,t,"revenue","revenue")}if(!0!==(null==s?void 0:s.silent)){const i=(0,d.isString)(null==s?void 0:s.name)?s.name:t.name,n=(0,d.isObject)(r)?r:{};await g(()=>e.track(i,n))}i._state=u}var A={type:"amplitude",config:{},async init({config:e,env:t}){var i;const n=null==(i=e.settings)?void 0:i.apiKey;if(!n)throw new d.StepError("Amplitude needs settings.apiKey",{code:"config"});const s={...e.settings,apiKey:n},r=y(t);r?await S(r,s):await S(f(),s).catch(e=>{throw u=void 0,e});return{...e,settings:{...s,_state:{lastIdentity:{}}}}},async push(e,{config:t,rule:i,env:n,data:s,collector:r,logger:o}){const a={apiKey:"",...t.settings},c=(null==i?void 0:i.settings)||{},u=y(n);await j(u||f(),e,a,c,i,s,r,o)},async on(e,{config:t,data:i,env:n,collector:s}){var r;const o=t.consent;if("consent"!==e||!o||!(0,d.isObject)(i))return;if(!Object.keys(o).some(e=>e in i))return;const a=!1!==(0,d.getGrantedConsent)(o,s.consent),c=null!=(r=y(n))?r:f();await(0,d.stepCall)(()=>c.setOptOut(!a),"consent-not-applied","Amplitude did not take the consent update")}},h=A;//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/types/index.ts"],"sourcesContent":["import type { WalkerOS } from '@walkeros/core';\nimport {\n getMappingValue,\n isArray,\n isDefined,\n isNumber,\n isObject,\n isString,\n} from '@walkeros/core';\nimport * as amplitudeSDK from '@amplitude/unified';\nimport type {\n AmplitudeSDK,\n Destination,\n Env,\n RuntimeState,\n Settings,\n} from './types';\n\n// Types export\nexport * as DestinationAmplitude from './types';\n\n/**\n * Resolve the Amplitude SDK: use the caller-provided mock when available\n * (tests), otherwise fall back to the real @amplitude/unified\n * namespace. Matches @walkeros/web-destination-clarity and\n * @walkeros/server-destination-gcp (BigQuery).\n */\nfunction getAmplitude(env: Env | undefined): AmplitudeSDK {\n return env?.amplitude ?? (amplitudeSDK as unknown as AmplitudeSDK);\n}\n\n/**\n * Deterministic string → positive integer hash (djb2).\n * Same input always produces the same output, so Amplitude's\n * setSessionId receives a stable number for cross-page-load session\n * continuity even when walkerOS sessions are string IDs.\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\n/**\n * Compare resolved identity against runtime state, call only the setters\n * that actually changed. Returns the updated state snapshot.\n */\nfunction applyIdentitySetters(\n amp: AmplitudeSDK,\n resolved: Record<string, unknown>,\n lastIdentity: RuntimeState['lastIdentity'] = {},\n): NonNullable<RuntimeState['lastIdentity']> {\n const updated = { ...lastIdentity };\n\n if ('user' in resolved) {\n const user = resolved.user;\n if (user === null || user === '') {\n amp.setUserId(undefined);\n delete updated.user;\n } else if (isString(user) && user !== lastIdentity.user) {\n amp.setUserId(user);\n updated.user = user;\n }\n }\n\n if ('device' in resolved) {\n const device = resolved.device;\n if (isString(device) && device !== lastIdentity.device) {\n amp.setDeviceId(device);\n updated.device = device;\n }\n }\n\n if ('session' in resolved) {\n const session = resolved.session;\n // Amplitude requires number (Unix ms or other integer). walkerOS\n // sessions are often strings (e.g. 's3ss10n'). Numeric strings parse\n // directly; non-numeric strings are hashed deterministically via\n // djb2 → same string always produces the same session number,\n // preserving cross-page-load consistency.\n const asNumber = isNumber(session)\n ? session\n : isString(session) && /^\\d+$/.test(session)\n ? Number(session)\n : isString(session)\n ? hashStringToInt(session)\n : undefined;\n if (asNumber !== undefined && asNumber !== lastIdentity.session) {\n amp.setSessionId(asNumber);\n updated.session = asNumber;\n }\n }\n\n return updated;\n}\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/**\n * Build a chained Identify instance from a resolved object's operation\n * keys. Returns undefined if no operations are present (so the caller\n * can skip amplitude.identify() entirely).\n */\nfunction buildIdentify(\n amp: AmplitudeSDK,\n resolved: Record<string, unknown>,\n): InstanceType<AmplitudeSDK['Identify']> | undefined {\n let identify: InstanceType<AmplitudeSDK['Identify']> | 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\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\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) => InstanceType<AmplitudeSDK['Revenue']>)(\n value,\n );\n }\n if (isObject(item.eventProperties)) {\n rev.setEventProperties(item.eventProperties as Record<string, unknown>);\n }\n return rev;\n}\n\nexport const destinationAmplitude: Destination = {\n type: 'amplitude',\n\n config: {},\n\n async init({ config, env }) {\n const settings = config.settings;\n if (!settings?.apiKey) return false;\n\n const amp = getAmplitude(env as Env | undefined);\n\n const {\n apiKey,\n identify: _identify,\n sessionReplay,\n experiment,\n engagement,\n _state: _existingState,\n ...browserOptions\n } = settings;\n\n const analyticsOptions = {\n autocapture: false,\n identityStorage: 'none' as const,\n ...browserOptions,\n };\n\n await amp.initAll(apiKey, {\n serverZone: analyticsOptions.serverZone,\n analytics: analyticsOptions,\n ...(sessionReplay ? { sessionReplay } : {}),\n ...(experiment?.deploymentKey ? { experiment } : {}),\n ...(engagement\n ? { engagement: engagement === true ? undefined : engagement }\n : {}),\n });\n\n const _state: RuntimeState = { lastIdentity: {} };\n return { ...config, settings: { ...settings, _state } };\n },\n\n async push(event, { config, rule, env, data, collector }) {\n const amp = getAmplitude(env as Env | undefined);\n const settings = (config.settings || {}) as Settings;\n const mappingSettings = rule?.settings || {};\n const state: RuntimeState = settings._state || {};\n\n // 1. Reset - fires first so subsequent identity calls start clean.\n if (mappingSettings.reset !== undefined) {\n const resolved =\n typeof mappingSettings.reset === 'boolean'\n ? mappingSettings.reset\n : await getMappingValue(event, mappingSettings.reset, {\n collector,\n });\n if (resolved) {\n amp.reset();\n state.lastIdentity = {};\n }\n }\n\n // 2. Identity setters - 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 state.lastIdentity = applyIdentitySetters(\n amp,\n identifyResolved,\n state.lastIdentity,\n );\n }\n }\n\n // 3. Identify operations - fires amplitude.identify() if the resolved\n // object has any op keys (set, setOnce, add, ...).\n if (identifyResolved) {\n const id = buildIdentify(amp, identifyResolved);\n if (id) amp.identify(id);\n }\n\n // 4. 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(type, name as string | string[]);\n }\n }\n }\n\n // 5. 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) amp.groupIdentify(type, name, id);\n }\n }\n }\n\n // 6. 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(buildRevenue(amp, item));\n }\n }\n\n // 7. Default track\n if (rule?.silent !== true) {\n const eventType = isString(rule?.name) ? rule.name : event.name;\n const eventProperties = isObject(data)\n ? (data as Record<string, unknown>)\n : {};\n amp.track(eventType, eventProperties);\n }\n\n // Persist state mutations back onto config.settings.\n settings._state = state;\n },\n\n on(type, context) {\n if (type !== 'consent' || !context.data) return;\n const amp = getAmplitude(context.env as Env | undefined);\n\n const consent = context.data as WalkerOS.Consent;\n // Derive the consent key from config.consent - iterate every key the\n // destination declared as required. If ALL required keys are granted,\n // opt IN; otherwise opt OUT. A missing config.consent means nothing\n // to check and we take no action.\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\nexport default destinationAmplitude;\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationWeb } from '@walkeros/web-core';\nimport type { Types as AmplitudeTypes } from '@amplitude/unified';\nimport type { SessionReplayOptions } from '@amplitude/plugin-session-replay-browser';\nimport type { ExperimentPluginConfig } from '@amplitude/plugin-experiment-browser';\nimport type { InitOptions as EngagementInitOptions } from '@amplitude/engagement-browser';\n\n// Amplitude's BrowserOptions has an `identify` field that conflicts with our\n// walkerOS mapping-based `identify`. Omit it so Settings can re-declare it.\ntype BrowserOptions = Omit<AmplitudeTypes.BrowserOptions, 'identify'>;\nexport type AmplitudeBrowserOptions = AmplitudeTypes.BrowserOptions;\n\n/**\n * Destination-level settings.\n *\n * All Amplitude `BrowserOptions` are passthrough init options — extending the\n * SDK type directly keeps IntelliSense complete and prevents drift. The\n * destination adds:\n * - `apiKey` (required) — first arg to `amplitude.init(...)`\n * - `identify` — destination-level identity mapping\n * - `sessionReplay` / `experiment` / `engagement` — optional plugin configs\n * - `_state` — runtime state (not user-facing, mutated by init/push)\n */\nexport interface Settings extends BrowserOptions {\n apiKey: string;\n identify?: WalkerOSMapping.Value;\n sessionReplay?: SessionReplayOptions;\n experiment?: ExperimentPluginConfig & { deploymentKey: string };\n engagement?: boolean | EngagementInitOptions;\n /** Runtime state — populated by init() and mutated by push(). Not user-facing. */\n _state?: RuntimeState;\n}\n\nexport interface RuntimeState {\n /** Last-set identity values, used to skip redundant setUserId/setDeviceId/setSessionId calls. */\n lastIdentity?: {\n user?: string;\n device?: string;\n session?: number;\n };\n}\n\nexport type InitSettings = Partial<Settings>;\n\n/**\n * Per-rule mapping settings. Every feature here is a walkerOS mapping value\n * resolved via getMappingValue(). Revenue uniquely supports loop to produce\n * an array of items → N amplitude.revenue() calls.\n */\nexport interface Mapping {\n identify?: WalkerOSMapping.Value;\n revenue?: WalkerOSMapping.Value;\n group?: WalkerOSMapping.Value;\n groupIdentify?: WalkerOSMapping.Value;\n reset?: WalkerOSMapping.Value | boolean;\n}\n\n/**\n * Amplitude SDK surface — the subset of @amplitude/unified the\n * destination actually uses. Mirrors the real module's named exports so\n * tests can mock each method individually via env.amplitude.\n */\nexport interface AmplitudeSDK {\n initAll: (apiKey: string, options?: Record<string, unknown>) => Promise<void>;\n track: (eventType: string, eventProperties?: Record<string, unknown>) => void;\n identify: (identify: IdentifyInstance) => void;\n revenue: (revenue: RevenueInstance) => void;\n reset: () => void;\n setOptOut: (optOut: boolean) => void;\n setUserId: (userId: string | undefined) => void;\n setDeviceId: (deviceId: string) => void;\n setSessionId: (sessionId: number) => void;\n setGroup: (groupType: string, groupName: string | string[]) => void;\n groupIdentify: (\n groupType: string,\n groupName: string,\n identify: IdentifyInstance,\n ) => void;\n flush: () => { promise: Promise<void> };\n add: (plugin: unknown) => { promise: Promise<void> };\n Identify: new () => IdentifyInstance;\n Revenue: new () => RevenueInstance;\n}\n\n/** Chainable Identify — mirrors @amplitude/unified 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/unified 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 * Env — optional SDK override. Production leaves this undefined and the\n * destination falls back to the real @amplitude/unified module\n * import. Tests provide a mock via env.amplitude = { ... }.\n */\nexport interface Env extends DestinationWeb.Env {\n amplitude?: AmplitudeSDK;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport type Destination = DestinationWeb.Destination<Types>;\nexport type Config = DestinationWeb.Config<Types>;\n\nexport interface AmplitudeDestination extends Destination {\n env?: Env;\n}\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/types/index.ts"],"sourcesContent":["import type { Collector, Logger, WalkerOS } from '@walkeros/core';\nimport {\n getGrantedConsent,\n getMappingValue,\n isArray,\n isDefined,\n isNumber,\n isObject,\n isString,\n StepError,\n stepCall,\n} from '@walkeros/core';\nimport * as amplitudeSDK from '@amplitude/unified';\nimport type {\n AmplitudePlugin,\n AmplitudeSDK,\n Destination,\n IdentifyInstance,\n Mapping,\n RevenueInstance,\n RuntimeState,\n Settings,\n} from './types';\n\n// Types export\nexport * as DestinationAmplitude from './types';\n\ntype RealSDK = AmplitudeSDK<amplitudeSDK.Identify, amplitudeSDK.Revenue>;\n\nlet real: RealSDK | undefined;\n\n/**\n * The real SDK: one @amplitude/unified client, as the module's own default\n * client is one per page. Created on first use; its `init` sets up analytics,\n * `add` one plugin at a time.\n */\nfunction realAmplitude(): RealSDK {\n if (real) return real;\n const client = amplitudeSDK.createInstance();\n real = {\n // The unified client forwards three arguments, and with three the\n // analytics SDK reads the second as a user id: the options go third.\n init: (apiKey, options) => client.init(apiKey, undefined, options),\n add: (plugin) => client.add(plugin),\n track: (eventType, eventProperties) =>\n client.track(eventType, eventProperties),\n identify: (identify) => client.identify(identify),\n revenue: (revenue) => client.revenue(revenue),\n reset: () => client.reset(),\n setOptOut: (optOut) => client.setOptOut(optOut),\n setUserId: (userId) => client.setUserId(userId),\n setDeviceId: (deviceId) => client.setDeviceId(deviceId),\n setSessionId: (sessionId) => client.setSessionId(sessionId),\n setGroup: (groupType, groupName) => client.setGroup(groupType, groupName),\n groupIdentify: (groupType, groupName, identify) =>\n client.groupIdentify(groupType, groupName, identify),\n Identify: amplitudeSDK.Identify,\n Revenue: amplitudeSDK.Revenue,\n };\n return real;\n}\n\n/** The caller-provided mock (tests), if the env carries one. */\nfunction mockOf(env: unknown): AmplitudeSDK | undefined {\n const amplitude = isObject(env) ? env.amplitude : undefined;\n if (amplitude === undefined || isAmplitudeSDK(amplitude)) return amplitude;\n throw new StepError('env.amplitude is not an Amplitude SDK', {\n code: 'config',\n });\n}\n\nfunction isAmplitudeSDK(value: unknown): value is AmplitudeSDK {\n return (\n isObject(value) &&\n [\n 'init',\n 'add',\n 'track',\n 'identify',\n 'revenue',\n 'reset',\n 'setOptOut',\n 'setUserId',\n 'setDeviceId',\n 'setSessionId',\n 'setGroup',\n 'groupIdentify',\n 'Identify',\n 'Revenue',\n ].every((key) => typeof value[key] === 'function')\n );\n}\n\nconst send = (fn: () => unknown) =>\n stepCall(fn, 'transport', 'Amplitude call failed');\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/** Waits for a call and the `promise` its AmplitudeReturn carries. */\nasync function settled(fn: () => unknown): Promise<void> {\n const result = await fn();\n if (isObject(result) && result.promise instanceof Promise)\n await result.promise;\n}\n\n/**\n * Deterministic string → positive integer hash (djb2).\n * Same input always produces the same output, so Amplitude's\n * setSessionId receives a stable number for cross-page-load session\n * continuity even when walkerOS sessions are string IDs.\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\n/**\n * Compare resolved identity against runtime state, call only the setters\n * that actually changed. Returns the updated state snapshot.\n */\nasync function applyIdentitySetters<\n I extends IdentifyInstance,\n R extends RevenueInstance,\n>(\n amp: AmplitudeSDK<I, R>,\n send: (fn: () => unknown) => Promise<unknown>,\n resolved: Record<string, unknown>,\n lastIdentity: RuntimeState['lastIdentity'] = {},\n): Promise<NonNullable<RuntimeState['lastIdentity']>> {\n const updated = { ...lastIdentity };\n\n if ('user' in resolved) {\n const user = resolved.user;\n if (user === null || user === '') {\n await send(() => amp.setUserId(undefined));\n delete updated.user;\n } else if (isString(user) && user !== lastIdentity.user) {\n await send(() => amp.setUserId(user));\n updated.user = user;\n }\n }\n\n if ('device' in resolved) {\n const device = resolved.device;\n if (isString(device) && device !== lastIdentity.device) {\n await send(() => amp.setDeviceId(device));\n updated.device = device;\n }\n }\n\n if ('session' in resolved) {\n const session = resolved.session;\n // Amplitude requires number (Unix ms or other integer). walkerOS\n // sessions are often strings (e.g. 's3ss10n'). Numeric strings parse\n // directly; non-numeric strings are hashed deterministically via\n // djb2 → same string always produces the same session number,\n // preserving cross-page-load consistency.\n const asNumber = isNumber(session)\n ? session\n : isString(session) && /^\\d+$/.test(session)\n ? Number(session)\n : isString(session)\n ? hashStringToInt(session)\n : undefined;\n if (asNumber !== undefined && asNumber !== lastIdentity.session) {\n await send(() => amp.setSessionId(asNumber));\n updated.session = asNumber;\n }\n }\n\n return updated;\n}\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/**\n * Build a chained Identify instance from a resolved object's operation\n * keys. Returns undefined if no operations are present (so the caller\n * can skip amplitude.identify() entirely).\n */\nfunction buildIdentify<I extends IdentifyInstance, R extends RevenueInstance>(\n amp: AmplitudeSDK<I, R>,\n resolved: Record<string, unknown>,\n): I | undefined {\n let identify: I | undefined;\n const ensure = (): I => {\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\n/** A Revenue instance from one resolved item, each field by its type. */\nfunction buildRevenue<I extends IdentifyInstance, R extends RevenueInstance>(\n amp: AmplitudeSDK<I, R>,\n item: Record<string, unknown>,\n): R {\n const rev = new amp.Revenue();\n const { productId, price, quantity, revenueType, currency, revenue } = item;\n const { receipt, receiptSig, eventProperties } = item;\n if (isString(productId)) rev.setProductId(productId);\n if (isNumber(price)) rev.setPrice(price);\n if (isNumber(quantity)) rev.setQuantity(quantity);\n if (isString(revenueType)) rev.setRevenueType(revenueType);\n if (isString(currency)) rev.setCurrency(currency);\n if (isNumber(revenue)) rev.setRevenue(revenue);\n if (isString(receipt)) rev.setReceipt(receipt);\n if (isString(receiptSig)) rev.setReceiptSig(receiptSig);\n if (isObject(eventProperties)) rev.setEventProperties(eventProperties);\n return rev;\n}\n\n/** Starts analytics, then each plugin the settings enable, in that order. */\nasync function start<I extends IdentifyInstance, R extends RevenueInstance>(\n amp: AmplitudeSDK<I, R>,\n settings: Settings,\n): Promise<void> {\n const {\n apiKey,\n identify: _identify,\n sessionReplay,\n experiment,\n engagement,\n _state: _existingState,\n ...browserOptions\n } = settings;\n\n const analyticsOptions = {\n autocapture: false,\n identityStorage: 'none' as const,\n ...browserOptions,\n };\n\n await stepCall(\n () => settled(() => amp.init(apiKey, analyticsOptions)),\n 'vendor-not-ready',\n 'Amplitude init failed',\n );\n\n // Session Replay, Experiment and Guides & Surveys are opt-in, each on its\n // own: only the ones the settings enable start, on the analytics server\n // zone and instance. Each package loads only then, with a dynamic import:\n // a static one of @amplitude/engagement-browser (an ESM entry without\n // \"type\": \"module\") can come in as CommonJS without its named exports\n // under Node loaders such as tsx, and failed every import of this module.\n const { serverZone, instanceName } = analyticsOptions;\n const shared = {\n ...(serverZone ? { serverZone } : {}),\n ...(instanceName ? { instanceName } : {}),\n };\n const plugins: Array<[string, () => Promise<AmplitudePlugin>]> = [];\n if (sessionReplay)\n plugins.push([\n 'Session Replay',\n async () => {\n const { sessionReplayPlugin } =\n await import('@amplitude/plugin-session-replay-browser');\n return sessionReplayPlugin({ ...sessionReplay, ...shared });\n },\n ]);\n if (experiment?.deploymentKey)\n plugins.push([\n 'Experiment',\n async () => {\n const { experimentPlugin } =\n await import('@amplitude/plugin-experiment-browser');\n return experimentPlugin({ ...experiment, ...shared });\n },\n ]);\n if (engagement)\n plugins.push([\n 'Guides & Surveys',\n async () => {\n const { plugin } = await import('@amplitude/engagement-browser');\n return plugin({\n ...(engagement === true ? {} : engagement),\n ...shared,\n });\n },\n ]);\n\n for (const [name, create] of plugins)\n await stepCall(\n async () => {\n const plugin = await create();\n await settled(() => amp.add(plugin));\n },\n 'vendor-not-ready',\n `Amplitude ${name} failed to start`,\n );\n}\n\nasync function pushTo<I extends IdentifyInstance, R extends RevenueInstance>(\n amp: AmplitudeSDK<I, R>,\n event: WalkerOS.Event,\n settings: Settings,\n mappingSettings: Mapping,\n rule: { name?: unknown; silent?: boolean } | undefined,\n data: unknown,\n collector: Collector.Instance,\n logger: Logger.Instance,\n): Promise<void> {\n const state: RuntimeState = settings._state || {};\n\n // 1. Reset - fires first so subsequent identity calls start clean.\n if (mappingSettings.reset !== undefined) {\n const resolved =\n typeof mappingSettings.reset === 'boolean'\n ? mappingSettings.reset\n : await getMappingValue(event, mappingSettings.reset, {\n collector,\n });\n if (resolved) {\n await send(() => amp.reset());\n state.lastIdentity = {};\n }\n }\n\n // 2. Identity setters - 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;\n state.lastIdentity = await applyIdentitySetters(\n amp,\n send,\n resolved,\n state.lastIdentity,\n );\n } else skipped(logger, event, 'identify', 'identify');\n }\n\n // 3. Identify operations - fires amplitude.identify() if the resolved\n // object has any op keys (set, setOnce, add, ...).\n if (identifyResolved) {\n const id = buildIdentify(amp, identifyResolved);\n if (id) await send(() => amp.identify(id));\n }\n\n // 4. Group assignment\n if (mappingSettings.group !== undefined) {\n const resolved = await getMappingValue(event, mappingSettings.group, {\n collector,\n });\n const { type, name } = isObject(resolved) ? resolved : {};\n if (isString(type) && (isString(name) || isStringArray(name)))\n await send(() => amp.setGroup(type, name));\n else skipped(logger, event, 'setGroup', 'group');\n }\n\n // 5. 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, name } = isObject(resolved) ? resolved : {};\n const id =\n isObject(resolved) && isString(type) && isString(name)\n ? buildIdentify(amp, resolved)\n : undefined;\n if (id && isString(type) && isString(name))\n await send(() => amp.groupIdentify(type, name, id));\n else skipped(logger, event, 'groupIdentify', 'groupIdentify');\n }\n\n // 6. 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 let sent = 0;\n for (const item of isArray(resolved) ? resolved : [resolved]) {\n if (!isObject(item)) continue;\n const revenue = buildRevenue(amp, item);\n await send(() => amp.revenue(revenue));\n sent++;\n }\n if (!sent) skipped(logger, event, 'revenue', 'revenue');\n }\n\n // 7. Default track\n if (rule?.silent !== true) {\n const eventType = isString(rule?.name) ? rule.name : event.name;\n const eventProperties = isObject(data) ? data : {};\n await send(() => amp.track(eventType, eventProperties));\n }\n\n // Persist state mutations back onto config.settings. A silent rule whose\n // settings resolve nothing (or an identity that is already current) sends\n // nothing: a declared skip, not a failure.\n settings._state = state;\n}\n\nfunction isStringArray(value: unknown): value is string[] {\n return isArray(value) && value.every(isString);\n}\n\nexport const destinationAmplitude: Destination = {\n type: 'amplitude',\n\n config: {},\n\n // The SDK starts in init, so it collects from page load on.\n async init({ config, env }) {\n const apiKey = config.settings?.apiKey;\n if (!apiKey)\n throw new StepError('Amplitude needs settings.apiKey', {\n code: 'config',\n });\n const settings: Settings = { ...config.settings, apiKey };\n\n const mock = mockOf(env);\n if (mock) await start(mock, settings);\n else\n await start(realAmplitude(), settings).catch((error: unknown) => {\n // The SDK answers a second init after a failed one as done without\n // starting: the next attempt gets a fresh client.\n real = undefined;\n throw error;\n });\n\n const _state: RuntimeState = { lastIdentity: {} };\n return { ...config, settings: { ...settings, _state } };\n },\n\n async push(event, { config, rule, env, data, collector, logger }) {\n const settings: Settings = { apiKey: '', ...config.settings };\n const mappingSettings: Mapping = rule?.settings || {};\n const mock = mockOf(env);\n await (mock\n ? pushTo(\n mock,\n event,\n settings,\n mappingSettings,\n rule,\n data,\n collector,\n logger,\n )\n : pushTo(\n realAmplitude(),\n event,\n settings,\n mappingSettings,\n rule,\n data,\n collector,\n logger,\n ));\n },\n\n // The destination's required consent decides, as it gates the pushes: a\n // revocation opts out of Amplitude, a grant opts back in.\n async on(type, { config, data, env, collector }) {\n const required = config.consent;\n if (type !== 'consent' || !required || !isObject(data)) return;\n if (!Object.keys(required).some((key) => key in data)) return;\n\n const granted = getGrantedConsent(required, collector.consent) !== false;\n const amp = mockOf(env) ?? realAmplitude();\n await stepCall(\n () => amp.setOptOut(!granted),\n 'consent-not-applied',\n 'Amplitude did not take the consent update',\n );\n },\n};\n\nexport default destinationAmplitude;\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationWeb } from '@walkeros/web-core';\nimport type { Types as AmplitudeTypes } from '@amplitude/unified';\nimport type { SessionReplayOptions } from '@amplitude/plugin-session-replay-browser';\nimport type { ExperimentPluginConfig } from '@amplitude/plugin-experiment-browser';\nimport type { InitOptions as EngagementInitOptions } from '@amplitude/engagement-browser';\n\n// Amplitude's BrowserOptions has an `identify` field that conflicts with our\n// walkerOS mapping-based `identify`. Omit it so Settings can re-declare it.\ntype BrowserOptions = Omit<AmplitudeTypes.BrowserOptions, 'identify'>;\nexport type AmplitudeBrowserOptions = AmplitudeTypes.BrowserOptions;\nexport type AmplitudePlugin = AmplitudeTypes.Plugin;\n\n/**\n * Destination-level settings.\n *\n * All Amplitude `BrowserOptions` are passthrough init options — extending the\n * SDK type directly keeps IntelliSense complete and prevents drift. The\n * destination adds:\n * - `apiKey` (required) — first arg to `amplitude.init(...)`\n * - `identify` — destination-level identity mapping\n * - `sessionReplay` / `experiment` / `engagement` — optional plugin configs\n * - `_state` — runtime state (not user-facing, mutated by init/push)\n */\nexport interface Settings extends BrowserOptions {\n apiKey: string;\n identify?: WalkerOSMapping.Value;\n sessionReplay?: SessionReplayOptions;\n experiment?: ExperimentPluginConfig & { deploymentKey: string };\n engagement?: boolean | EngagementInitOptions;\n /** Runtime state — populated by init() and mutated by push(). Not user-facing. */\n _state?: RuntimeState;\n}\n\nexport interface RuntimeState {\n /** Last-set identity values, used to skip redundant setUserId/setDeviceId/setSessionId calls. */\n lastIdentity?: {\n user?: string;\n device?: string;\n session?: number;\n };\n}\n\nexport type InitSettings = Partial<Settings>;\n\n/**\n * Per-rule mapping settings. Every feature here is a walkerOS mapping value\n * resolved via getMappingValue(). Revenue uniquely supports loop to produce\n * an array of items → N amplitude.revenue() calls.\n */\nexport interface Mapping {\n identify?: WalkerOSMapping.Value;\n revenue?: WalkerOSMapping.Value;\n group?: WalkerOSMapping.Value;\n groupIdentify?: WalkerOSMapping.Value;\n reset?: WalkerOSMapping.Value | boolean;\n}\n\n/**\n * Amplitude SDK surface: the subset of @amplitude/unified the\n * destination actually uses. Mirrors the real client's methods so tests can\n * mock each one via env.amplitude. Generic over the Identify and Revenue\n * instances, so the real SDK gets back the instances it created.\n */\nexport interface AmplitudeSDK<\n I extends IdentifyInstance = IdentifyInstance,\n R extends RevenueInstance = RevenueInstance,\n> {\n /** Analytics. */\n init: (apiKey: string, options?: AmplitudeBrowserOptions) => unknown;\n /** One plugin: Session Replay, Experiment or Guides & Surveys. */\n add: (plugin: AmplitudePlugin) => unknown;\n track: (\n eventType: string,\n eventProperties?: Record<string, unknown>,\n ) => unknown;\n identify: (identify: I) => unknown;\n revenue: (revenue: R) => unknown;\n reset: () => unknown;\n setOptOut: (optOut: boolean) => unknown;\n setUserId: (userId: string | undefined) => unknown;\n setDeviceId: (deviceId: string) => unknown;\n setSessionId: (sessionId: number) => unknown;\n setGroup: (groupType: string, groupName: string | string[]) => unknown;\n groupIdentify: (groupType: string, groupName: string, identify: I) => unknown;\n Identify: new () => I;\n Revenue: new () => R;\n}\n\n/** Chainable Identify, as the @amplitude/unified 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, as the @amplitude/unified 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 * Env — optional SDK override. Production leaves this undefined and the\n * destination falls back to the real @amplitude/unified module\n * import. Tests provide a mock via env.amplitude = { ... }.\n */\nexport interface Env extends DestinationWeb.Env {\n amplitude?: AmplitudeSDK;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport type Destination = DestinationWeb.Destination<Types>;\nexport type Config = DestinationWeb.Config<Types>;\n\nexport interface AmplitudeDestination extends Destination {\n env?: Env;\n}\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n"],"mappings":"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","names":["send"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{getGrantedConsent as e,getMappingValue as t,isArray as n,isDefined as i,isNumber as s,isObject as o,isString as r,StepError as a,stepCall as c}from"@walkeros/core";import*as d from"@amplitude/unified";var u,p={};function l(){if(u)return u;const e=d.createInstance();return u={init:(t,n)=>e.init(t,void 0,n),add:t=>e.add(t),track:(t,n)=>e.track(t,n),identify:t=>e.identify(t),revenue:t=>e.revenue(t),reset:()=>e.reset(),setOptOut:t=>e.setOptOut(t),setUserId:t=>e.setUserId(t),setDeviceId:t=>e.setDeviceId(t),setSessionId:t=>e.setSessionId(t),setGroup:(t,n)=>e.setGroup(t,n),groupIdentify:(t,n,i)=>e.groupIdentify(t,n,i),Identify:d.Identify,Revenue:d.Revenue}}function f(e){const t=o(e)?e.amplitude:void 0;if(void 0===t||o(n=t)&&["init","add","track","identify","revenue","reset","setOptOut","setUserId","setDeviceId","setSessionId","setGroup","groupIdentify","Identify","Revenue"].every(e=>"function"==typeof n[e]))return t;var n;throw new a("env.amplitude is not an Amplitude SDK",{code:"config"})}var v=e=>c(e,"transport","Amplitude call failed");function y(e,t,n,i){e.debug(`Amplitude skipped ${n}: ${i} resolved to nothing`,{event:t.name})}async function m(e){const t=await e();o(t)&&t.promise instanceof Promise&&await t.promise}async function g(e,t,n,i={}){const o={...i};if("user"in n){const s=n.user;null===s||""===s?(await t(()=>e.setUserId(void 0)),delete o.user):r(s)&&s!==i.user&&(await t(()=>e.setUserId(s)),o.user=s)}if("device"in n){const s=n.device;r(s)&&s!==i.device&&(await t(()=>e.setDeviceId(s)),o.device=s)}if("session"in n){const a=n.session,c=s(a)?a:r(a)&&/^\d+$/.test(a)?Number(a):r(a)?function(e){let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e.charCodeAt(n)|0;return Math.abs(t)}(a):void 0;void 0!==c&&c!==i.session&&(await t(()=>e.setSessionId(c)),o.session=c)}return o}var w=["set","setOnce","add","append","prepend","preInsert","postInsert","remove"];function I(e,t){let a;const c=()=>(a||(a=new e.Identify),a);for(const e of w){const n=t[e];if(o(n))for(const[t,o]of Object.entries(n))i(o)&&("add"===e?s(o)&&c().add(t,o):c()[e](t,o))}if(n(t.unset))for(const e of t.unset)r(e)&&c().unset(e);return!0===t.clearAll&&c().clearAll(),a}function h(e,t){const n=new e.Revenue,{productId:i,price:a,quantity:c,revenueType:d,currency:u,revenue:p}=t,{receipt:l,receiptSig:f,eventProperties:v}=t;return r(i)&&n.setProductId(i),s(a)&&n.setPrice(a),s(c)&&n.setQuantity(c),r(d)&&n.setRevenueType(d),r(u)&&n.setCurrency(u),s(p)&&n.setRevenue(p),r(l)&&n.setReceipt(l),r(f)&&n.setReceiptSig(f),o(v)&&n.setEventProperties(v),n}async function A(e,t){const{apiKey:n,identify:i,sessionReplay:s,experiment:o,engagement:r,_state:a,...d}=t,u={autocapture:!1,identityStorage:"none",...d};await c(()=>m(()=>e.init(n,u)),"vendor-not-ready","Amplitude init failed");const{serverZone:p,instanceName:l}=u,f={...p?{serverZone:p}:{},...l?{instanceName:l}:{}},v=[];s&&v.push(["Session Replay",async()=>{const{sessionReplayPlugin:e}=await import("@amplitude/plugin-session-replay-browser");return e({...s,...f})}]),(null==o?void 0:o.deploymentKey)&&v.push(["Experiment",async()=>{const{experimentPlugin:e}=await import("@amplitude/plugin-experiment-browser");return e({...o,...f})}]),r&&v.push(["Guides & Surveys",async()=>{const{plugin:e}=await import("@amplitude/engagement-browser");return e({...!0===r?{}:r,...f})}]);for(const[t,n]of v)await c(async()=>{const t=await n();await m(()=>e.add(t))},"vendor-not-ready",`Amplitude ${t} failed to start`)}async function O(e,i,s,a,c,d,u,p){var l;const f=s._state||{};if(void 0!==a.reset){("boolean"==typeof a.reset?a.reset:await t(i,a.reset,{collector:u}))&&(await v(()=>e.reset()),f.lastIdentity={})}const m=null!=(l=a.identify)?l:s.identify;let w;if(void 0!==m){const n=await t(i,m,{collector:u});o(n)?(w=n,f.lastIdentity=await g(e,v,n,f.lastIdentity)):y(p,i,"identify","identify")}if(w){const t=I(e,w);t&&await v(()=>e.identify(t))}if(void 0!==a.group){const s=await t(i,a.group,{collector:u}),{type:c,name:d}=o(s)?s:{};r(c)&&(r(d)||n(A=d)&&A.every(r))?await v(()=>e.setGroup(c,d)):y(p,i,"setGroup","group")}var A;if(void 0!==a.groupIdentify){const n=await t(i,a.groupIdentify,{collector:u}),{type:s,name:c}=o(n)?n:{},d=o(n)&&r(s)&&r(c)?I(e,n):void 0;d&&r(s)&&r(c)?await v(()=>e.groupIdentify(s,c,d)):y(p,i,"groupIdentify","groupIdentify")}if(void 0!==a.revenue){const s=await t(i,a.revenue,{collector:u});let r=0;for(const t of n(s)?s:[s]){if(!o(t))continue;const n=h(e,t);await v(()=>e.revenue(n)),r++}r||y(p,i,"revenue","revenue")}if(!0!==(null==c?void 0:c.silent)){const t=r(null==c?void 0:c.name)?c.name:i.name,n=o(d)?d:{};await v(()=>e.track(t,n))}s._state=f}var R={type:"amplitude",config:{},async init({config:e,env:t}){var n;const i=null==(n=e.settings)?void 0:n.apiKey;if(!i)throw new a("Amplitude needs settings.apiKey",{code:"config"});const s={...e.settings,apiKey:i},o=f(t);o?await A(o,s):await A(l(),s).catch(e=>{throw u=void 0,e});return{...e,settings:{...s,_state:{lastIdentity:{}}}}},async push(e,{config:t,rule:n,env:i,data:s,collector:o,logger:r}){const a={apiKey:"",...t.settings},c=(null==n?void 0:n.settings)||{},d=f(i);await O(d||l(),e,a,c,n,s,o,r)},async on(t,{config:n,data:i,env:s,collector:r}){var a;const d=n.consent;if("consent"!==t||!d||!o(i))return;if(!Object.keys(d).some(e=>e in i))return;const u=!1!==e(d,r.consent),p=null!=(a=f(s))?a:l();await c(()=>p.setOptOut(!u),"consent-not-applied","Amplitude did not take the consent update")}},S=R;export{p as DestinationAmplitude,S as default,R as destinationAmplitude};//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/types/index.ts"],"sourcesContent":["import type { WalkerOS } from '@walkeros/core';\nimport {\n getMappingValue,\n isArray,\n isDefined,\n isNumber,\n isObject,\n isString,\n} from '@walkeros/core';\nimport * as amplitudeSDK from '@amplitude/unified';\nimport type {\n AmplitudeSDK,\n Destination,\n Env,\n RuntimeState,\n Settings,\n} from './types';\n\n// Types export\nexport * as DestinationAmplitude from './types';\n\n/**\n * Resolve the Amplitude SDK: use the caller-provided mock when available\n * (tests), otherwise fall back to the real @amplitude/unified\n * namespace. Matches @walkeros/web-destination-clarity and\n * @walkeros/server-destination-gcp (BigQuery).\n */\nfunction getAmplitude(env: Env | undefined): AmplitudeSDK {\n return env?.amplitude ?? (amplitudeSDK as unknown as AmplitudeSDK);\n}\n\n/**\n * Deterministic string → positive integer hash (djb2).\n * Same input always produces the same output, so Amplitude's\n * setSessionId receives a stable number for cross-page-load session\n * continuity even when walkerOS sessions are string IDs.\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\n/**\n * Compare resolved identity against runtime state, call only the setters\n * that actually changed. Returns the updated state snapshot.\n */\nfunction applyIdentitySetters(\n amp: AmplitudeSDK,\n resolved: Record<string, unknown>,\n lastIdentity: RuntimeState['lastIdentity'] = {},\n): NonNullable<RuntimeState['lastIdentity']> {\n const updated = { ...lastIdentity };\n\n if ('user' in resolved) {\n const user = resolved.user;\n if (user === null || user === '') {\n amp.setUserId(undefined);\n delete updated.user;\n } else if (isString(user) && user !== lastIdentity.user) {\n amp.setUserId(user);\n updated.user = user;\n }\n }\n\n if ('device' in resolved) {\n const device = resolved.device;\n if (isString(device) && device !== lastIdentity.device) {\n amp.setDeviceId(device);\n updated.device = device;\n }\n }\n\n if ('session' in resolved) {\n const session = resolved.session;\n // Amplitude requires number (Unix ms or other integer). walkerOS\n // sessions are often strings (e.g. 's3ss10n'). Numeric strings parse\n // directly; non-numeric strings are hashed deterministically via\n // djb2 → same string always produces the same session number,\n // preserving cross-page-load consistency.\n const asNumber = isNumber(session)\n ? session\n : isString(session) && /^\\d+$/.test(session)\n ? Number(session)\n : isString(session)\n ? hashStringToInt(session)\n : undefined;\n if (asNumber !== undefined && asNumber !== lastIdentity.session) {\n amp.setSessionId(asNumber);\n updated.session = asNumber;\n }\n }\n\n return updated;\n}\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/**\n * Build a chained Identify instance from a resolved object's operation\n * keys. Returns undefined if no operations are present (so the caller\n * can skip amplitude.identify() entirely).\n */\nfunction buildIdentify(\n amp: AmplitudeSDK,\n resolved: Record<string, unknown>,\n): InstanceType<AmplitudeSDK['Identify']> | undefined {\n let identify: InstanceType<AmplitudeSDK['Identify']> | 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\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\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) => InstanceType<AmplitudeSDK['Revenue']>)(\n value,\n );\n }\n if (isObject(item.eventProperties)) {\n rev.setEventProperties(item.eventProperties as Record<string, unknown>);\n }\n return rev;\n}\n\nexport const destinationAmplitude: Destination = {\n type: 'amplitude',\n\n config: {},\n\n async init({ config, env }) {\n const settings = config.settings;\n if (!settings?.apiKey) return false;\n\n const amp = getAmplitude(env as Env | undefined);\n\n const {\n apiKey,\n identify: _identify,\n sessionReplay,\n experiment,\n engagement,\n _state: _existingState,\n ...browserOptions\n } = settings;\n\n const analyticsOptions = {\n autocapture: false,\n identityStorage: 'none' as const,\n ...browserOptions,\n };\n\n await amp.initAll(apiKey, {\n serverZone: analyticsOptions.serverZone,\n analytics: analyticsOptions,\n ...(sessionReplay ? { sessionReplay } : {}),\n ...(experiment?.deploymentKey ? { experiment } : {}),\n ...(engagement\n ? { engagement: engagement === true ? undefined : engagement }\n : {}),\n });\n\n const _state: RuntimeState = { lastIdentity: {} };\n return { ...config, settings: { ...settings, _state } };\n },\n\n async push(event, { config, rule, env, data, collector }) {\n const amp = getAmplitude(env as Env | undefined);\n const settings = (config.settings || {}) as Settings;\n const mappingSettings = rule?.settings || {};\n const state: RuntimeState = settings._state || {};\n\n // 1. Reset - fires first so subsequent identity calls start clean.\n if (mappingSettings.reset !== undefined) {\n const resolved =\n typeof mappingSettings.reset === 'boolean'\n ? mappingSettings.reset\n : await getMappingValue(event, mappingSettings.reset, {\n collector,\n });\n if (resolved) {\n amp.reset();\n state.lastIdentity = {};\n }\n }\n\n // 2. Identity setters - 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 state.lastIdentity = applyIdentitySetters(\n amp,\n identifyResolved,\n state.lastIdentity,\n );\n }\n }\n\n // 3. Identify operations - fires amplitude.identify() if the resolved\n // object has any op keys (set, setOnce, add, ...).\n if (identifyResolved) {\n const id = buildIdentify(amp, identifyResolved);\n if (id) amp.identify(id);\n }\n\n // 4. 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(type, name as string | string[]);\n }\n }\n }\n\n // 5. 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) amp.groupIdentify(type, name, id);\n }\n }\n }\n\n // 6. 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(buildRevenue(amp, item));\n }\n }\n\n // 7. Default track\n if (rule?.silent !== true) {\n const eventType = isString(rule?.name) ? rule.name : event.name;\n const eventProperties = isObject(data)\n ? (data as Record<string, unknown>)\n : {};\n amp.track(eventType, eventProperties);\n }\n\n // Persist state mutations back onto config.settings.\n settings._state = state;\n },\n\n on(type, context) {\n if (type !== 'consent' || !context.data) return;\n const amp = getAmplitude(context.env as Env | undefined);\n\n const consent = context.data as WalkerOS.Consent;\n // Derive the consent key from config.consent - iterate every key the\n // destination declared as required. If ALL required keys are granted,\n // opt IN; otherwise opt OUT. A missing config.consent means nothing\n // to check and we take no action.\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\nexport default destinationAmplitude;\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationWeb } from '@walkeros/web-core';\nimport type { Types as AmplitudeTypes } from '@amplitude/unified';\nimport type { SessionReplayOptions } from '@amplitude/plugin-session-replay-browser';\nimport type { ExperimentPluginConfig } from '@amplitude/plugin-experiment-browser';\nimport type { InitOptions as EngagementInitOptions } from '@amplitude/engagement-browser';\n\n// Amplitude's BrowserOptions has an `identify` field that conflicts with our\n// walkerOS mapping-based `identify`. Omit it so Settings can re-declare it.\ntype BrowserOptions = Omit<AmplitudeTypes.BrowserOptions, 'identify'>;\nexport type AmplitudeBrowserOptions = AmplitudeTypes.BrowserOptions;\n\n/**\n * Destination-level settings.\n *\n * All Amplitude `BrowserOptions` are passthrough init options — extending the\n * SDK type directly keeps IntelliSense complete and prevents drift. The\n * destination adds:\n * - `apiKey` (required) — first arg to `amplitude.init(...)`\n * - `identify` — destination-level identity mapping\n * - `sessionReplay` / `experiment` / `engagement` — optional plugin configs\n * - `_state` — runtime state (not user-facing, mutated by init/push)\n */\nexport interface Settings extends BrowserOptions {\n apiKey: string;\n identify?: WalkerOSMapping.Value;\n sessionReplay?: SessionReplayOptions;\n experiment?: ExperimentPluginConfig & { deploymentKey: string };\n engagement?: boolean | EngagementInitOptions;\n /** Runtime state — populated by init() and mutated by push(). Not user-facing. */\n _state?: RuntimeState;\n}\n\nexport interface RuntimeState {\n /** Last-set identity values, used to skip redundant setUserId/setDeviceId/setSessionId calls. */\n lastIdentity?: {\n user?: string;\n device?: string;\n session?: number;\n };\n}\n\nexport type InitSettings = Partial<Settings>;\n\n/**\n * Per-rule mapping settings. Every feature here is a walkerOS mapping value\n * resolved via getMappingValue(). Revenue uniquely supports loop to produce\n * an array of items → N amplitude.revenue() calls.\n */\nexport interface Mapping {\n identify?: WalkerOSMapping.Value;\n revenue?: WalkerOSMapping.Value;\n group?: WalkerOSMapping.Value;\n groupIdentify?: WalkerOSMapping.Value;\n reset?: WalkerOSMapping.Value | boolean;\n}\n\n/**\n * Amplitude SDK surface — the subset of @amplitude/unified the\n * destination actually uses. Mirrors the real module's named exports so\n * tests can mock each method individually via env.amplitude.\n */\nexport interface AmplitudeSDK {\n initAll: (apiKey: string, options?: Record<string, unknown>) => Promise<void>;\n track: (eventType: string, eventProperties?: Record<string, unknown>) => void;\n identify: (identify: IdentifyInstance) => void;\n revenue: (revenue: RevenueInstance) => void;\n reset: () => void;\n setOptOut: (optOut: boolean) => void;\n setUserId: (userId: string | undefined) => void;\n setDeviceId: (deviceId: string) => void;\n setSessionId: (sessionId: number) => void;\n setGroup: (groupType: string, groupName: string | string[]) => void;\n groupIdentify: (\n groupType: string,\n groupName: string,\n identify: IdentifyInstance,\n ) => void;\n flush: () => { promise: Promise<void> };\n add: (plugin: unknown) => { promise: Promise<void> };\n Identify: new () => IdentifyInstance;\n Revenue: new () => RevenueInstance;\n}\n\n/** Chainable Identify — mirrors @amplitude/unified 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/unified 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 * Env — optional SDK override. Production leaves this undefined and the\n * destination falls back to the real @amplitude/unified module\n * import. Tests provide a mock via env.amplitude = { ... }.\n */\nexport interface Env extends DestinationWeb.Env {\n amplitude?: AmplitudeSDK;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport type Destination = DestinationWeb.Destination<Types>;\nexport type Config = DestinationWeb.Config<Types>;\n\nexport interface AmplitudeDestination extends Destination {\n env?: Env;\n}\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/types/index.ts"],"sourcesContent":["import type { Collector, Logger, WalkerOS } from '@walkeros/core';\nimport {\n getGrantedConsent,\n getMappingValue,\n isArray,\n isDefined,\n isNumber,\n isObject,\n isString,\n StepError,\n stepCall,\n} from '@walkeros/core';\nimport * as amplitudeSDK from '@amplitude/unified';\nimport type {\n AmplitudePlugin,\n AmplitudeSDK,\n Destination,\n IdentifyInstance,\n Mapping,\n RevenueInstance,\n RuntimeState,\n Settings,\n} from './types';\n\n// Types export\nexport * as DestinationAmplitude from './types';\n\ntype RealSDK = AmplitudeSDK<amplitudeSDK.Identify, amplitudeSDK.Revenue>;\n\nlet real: RealSDK | undefined;\n\n/**\n * The real SDK: one @amplitude/unified client, as the module's own default\n * client is one per page. Created on first use; its `init` sets up analytics,\n * `add` one plugin at a time.\n */\nfunction realAmplitude(): RealSDK {\n if (real) return real;\n const client = amplitudeSDK.createInstance();\n real = {\n // The unified client forwards three arguments, and with three the\n // analytics SDK reads the second as a user id: the options go third.\n init: (apiKey, options) => client.init(apiKey, undefined, options),\n add: (plugin) => client.add(plugin),\n track: (eventType, eventProperties) =>\n client.track(eventType, eventProperties),\n identify: (identify) => client.identify(identify),\n revenue: (revenue) => client.revenue(revenue),\n reset: () => client.reset(),\n setOptOut: (optOut) => client.setOptOut(optOut),\n setUserId: (userId) => client.setUserId(userId),\n setDeviceId: (deviceId) => client.setDeviceId(deviceId),\n setSessionId: (sessionId) => client.setSessionId(sessionId),\n setGroup: (groupType, groupName) => client.setGroup(groupType, groupName),\n groupIdentify: (groupType, groupName, identify) =>\n client.groupIdentify(groupType, groupName, identify),\n Identify: amplitudeSDK.Identify,\n Revenue: amplitudeSDK.Revenue,\n };\n return real;\n}\n\n/** The caller-provided mock (tests), if the env carries one. */\nfunction mockOf(env: unknown): AmplitudeSDK | undefined {\n const amplitude = isObject(env) ? env.amplitude : undefined;\n if (amplitude === undefined || isAmplitudeSDK(amplitude)) return amplitude;\n throw new StepError('env.amplitude is not an Amplitude SDK', {\n code: 'config',\n });\n}\n\nfunction isAmplitudeSDK(value: unknown): value is AmplitudeSDK {\n return (\n isObject(value) &&\n [\n 'init',\n 'add',\n 'track',\n 'identify',\n 'revenue',\n 'reset',\n 'setOptOut',\n 'setUserId',\n 'setDeviceId',\n 'setSessionId',\n 'setGroup',\n 'groupIdentify',\n 'Identify',\n 'Revenue',\n ].every((key) => typeof value[key] === 'function')\n );\n}\n\nconst send = (fn: () => unknown) =>\n stepCall(fn, 'transport', 'Amplitude call failed');\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/** Waits for a call and the `promise` its AmplitudeReturn carries. */\nasync function settled(fn: () => unknown): Promise<void> {\n const result = await fn();\n if (isObject(result) && result.promise instanceof Promise)\n await result.promise;\n}\n\n/**\n * Deterministic string → positive integer hash (djb2).\n * Same input always produces the same output, so Amplitude's\n * setSessionId receives a stable number for cross-page-load session\n * continuity even when walkerOS sessions are string IDs.\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\n/**\n * Compare resolved identity against runtime state, call only the setters\n * that actually changed. Returns the updated state snapshot.\n */\nasync function applyIdentitySetters<\n I extends IdentifyInstance,\n R extends RevenueInstance,\n>(\n amp: AmplitudeSDK<I, R>,\n send: (fn: () => unknown) => Promise<unknown>,\n resolved: Record<string, unknown>,\n lastIdentity: RuntimeState['lastIdentity'] = {},\n): Promise<NonNullable<RuntimeState['lastIdentity']>> {\n const updated = { ...lastIdentity };\n\n if ('user' in resolved) {\n const user = resolved.user;\n if (user === null || user === '') {\n await send(() => amp.setUserId(undefined));\n delete updated.user;\n } else if (isString(user) && user !== lastIdentity.user) {\n await send(() => amp.setUserId(user));\n updated.user = user;\n }\n }\n\n if ('device' in resolved) {\n const device = resolved.device;\n if (isString(device) && device !== lastIdentity.device) {\n await send(() => amp.setDeviceId(device));\n updated.device = device;\n }\n }\n\n if ('session' in resolved) {\n const session = resolved.session;\n // Amplitude requires number (Unix ms or other integer). walkerOS\n // sessions are often strings (e.g. 's3ss10n'). Numeric strings parse\n // directly; non-numeric strings are hashed deterministically via\n // djb2 → same string always produces the same session number,\n // preserving cross-page-load consistency.\n const asNumber = isNumber(session)\n ? session\n : isString(session) && /^\\d+$/.test(session)\n ? Number(session)\n : isString(session)\n ? hashStringToInt(session)\n : undefined;\n if (asNumber !== undefined && asNumber !== lastIdentity.session) {\n await send(() => amp.setSessionId(asNumber));\n updated.session = asNumber;\n }\n }\n\n return updated;\n}\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/**\n * Build a chained Identify instance from a resolved object's operation\n * keys. Returns undefined if no operations are present (so the caller\n * can skip amplitude.identify() entirely).\n */\nfunction buildIdentify<I extends IdentifyInstance, R extends RevenueInstance>(\n amp: AmplitudeSDK<I, R>,\n resolved: Record<string, unknown>,\n): I | undefined {\n let identify: I | undefined;\n const ensure = (): I => {\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\n/** A Revenue instance from one resolved item, each field by its type. */\nfunction buildRevenue<I extends IdentifyInstance, R extends RevenueInstance>(\n amp: AmplitudeSDK<I, R>,\n item: Record<string, unknown>,\n): R {\n const rev = new amp.Revenue();\n const { productId, price, quantity, revenueType, currency, revenue } = item;\n const { receipt, receiptSig, eventProperties } = item;\n if (isString(productId)) rev.setProductId(productId);\n if (isNumber(price)) rev.setPrice(price);\n if (isNumber(quantity)) rev.setQuantity(quantity);\n if (isString(revenueType)) rev.setRevenueType(revenueType);\n if (isString(currency)) rev.setCurrency(currency);\n if (isNumber(revenue)) rev.setRevenue(revenue);\n if (isString(receipt)) rev.setReceipt(receipt);\n if (isString(receiptSig)) rev.setReceiptSig(receiptSig);\n if (isObject(eventProperties)) rev.setEventProperties(eventProperties);\n return rev;\n}\n\n/** Starts analytics, then each plugin the settings enable, in that order. */\nasync function start<I extends IdentifyInstance, R extends RevenueInstance>(\n amp: AmplitudeSDK<I, R>,\n settings: Settings,\n): Promise<void> {\n const {\n apiKey,\n identify: _identify,\n sessionReplay,\n experiment,\n engagement,\n _state: _existingState,\n ...browserOptions\n } = settings;\n\n const analyticsOptions = {\n autocapture: false,\n identityStorage: 'none' as const,\n ...browserOptions,\n };\n\n await stepCall(\n () => settled(() => amp.init(apiKey, analyticsOptions)),\n 'vendor-not-ready',\n 'Amplitude init failed',\n );\n\n // Session Replay, Experiment and Guides & Surveys are opt-in, each on its\n // own: only the ones the settings enable start, on the analytics server\n // zone and instance. Each package loads only then, with a dynamic import:\n // a static one of @amplitude/engagement-browser (an ESM entry without\n // \"type\": \"module\") can come in as CommonJS without its named exports\n // under Node loaders such as tsx, and failed every import of this module.\n const { serverZone, instanceName } = analyticsOptions;\n const shared = {\n ...(serverZone ? { serverZone } : {}),\n ...(instanceName ? { instanceName } : {}),\n };\n const plugins: Array<[string, () => Promise<AmplitudePlugin>]> = [];\n if (sessionReplay)\n plugins.push([\n 'Session Replay',\n async () => {\n const { sessionReplayPlugin } =\n await import('@amplitude/plugin-session-replay-browser');\n return sessionReplayPlugin({ ...sessionReplay, ...shared });\n },\n ]);\n if (experiment?.deploymentKey)\n plugins.push([\n 'Experiment',\n async () => {\n const { experimentPlugin } =\n await import('@amplitude/plugin-experiment-browser');\n return experimentPlugin({ ...experiment, ...shared });\n },\n ]);\n if (engagement)\n plugins.push([\n 'Guides & Surveys',\n async () => {\n const { plugin } = await import('@amplitude/engagement-browser');\n return plugin({\n ...(engagement === true ? {} : engagement),\n ...shared,\n });\n },\n ]);\n\n for (const [name, create] of plugins)\n await stepCall(\n async () => {\n const plugin = await create();\n await settled(() => amp.add(plugin));\n },\n 'vendor-not-ready',\n `Amplitude ${name} failed to start`,\n );\n}\n\nasync function pushTo<I extends IdentifyInstance, R extends RevenueInstance>(\n amp: AmplitudeSDK<I, R>,\n event: WalkerOS.Event,\n settings: Settings,\n mappingSettings: Mapping,\n rule: { name?: unknown; silent?: boolean } | undefined,\n data: unknown,\n collector: Collector.Instance,\n logger: Logger.Instance,\n): Promise<void> {\n const state: RuntimeState = settings._state || {};\n\n // 1. Reset - fires first so subsequent identity calls start clean.\n if (mappingSettings.reset !== undefined) {\n const resolved =\n typeof mappingSettings.reset === 'boolean'\n ? mappingSettings.reset\n : await getMappingValue(event, mappingSettings.reset, {\n collector,\n });\n if (resolved) {\n await send(() => amp.reset());\n state.lastIdentity = {};\n }\n }\n\n // 2. Identity setters - 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;\n state.lastIdentity = await applyIdentitySetters(\n amp,\n send,\n resolved,\n state.lastIdentity,\n );\n } else skipped(logger, event, 'identify', 'identify');\n }\n\n // 3. Identify operations - fires amplitude.identify() if the resolved\n // object has any op keys (set, setOnce, add, ...).\n if (identifyResolved) {\n const id = buildIdentify(amp, identifyResolved);\n if (id) await send(() => amp.identify(id));\n }\n\n // 4. Group assignment\n if (mappingSettings.group !== undefined) {\n const resolved = await getMappingValue(event, mappingSettings.group, {\n collector,\n });\n const { type, name } = isObject(resolved) ? resolved : {};\n if (isString(type) && (isString(name) || isStringArray(name)))\n await send(() => amp.setGroup(type, name));\n else skipped(logger, event, 'setGroup', 'group');\n }\n\n // 5. 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, name } = isObject(resolved) ? resolved : {};\n const id =\n isObject(resolved) && isString(type) && isString(name)\n ? buildIdentify(amp, resolved)\n : undefined;\n if (id && isString(type) && isString(name))\n await send(() => amp.groupIdentify(type, name, id));\n else skipped(logger, event, 'groupIdentify', 'groupIdentify');\n }\n\n // 6. 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 let sent = 0;\n for (const item of isArray(resolved) ? resolved : [resolved]) {\n if (!isObject(item)) continue;\n const revenue = buildRevenue(amp, item);\n await send(() => amp.revenue(revenue));\n sent++;\n }\n if (!sent) skipped(logger, event, 'revenue', 'revenue');\n }\n\n // 7. Default track\n if (rule?.silent !== true) {\n const eventType = isString(rule?.name) ? rule.name : event.name;\n const eventProperties = isObject(data) ? data : {};\n await send(() => amp.track(eventType, eventProperties));\n }\n\n // Persist state mutations back onto config.settings. A silent rule whose\n // settings resolve nothing (or an identity that is already current) sends\n // nothing: a declared skip, not a failure.\n settings._state = state;\n}\n\nfunction isStringArray(value: unknown): value is string[] {\n return isArray(value) && value.every(isString);\n}\n\nexport const destinationAmplitude: Destination = {\n type: 'amplitude',\n\n config: {},\n\n // The SDK starts in init, so it collects from page load on.\n async init({ config, env }) {\n const apiKey = config.settings?.apiKey;\n if (!apiKey)\n throw new StepError('Amplitude needs settings.apiKey', {\n code: 'config',\n });\n const settings: Settings = { ...config.settings, apiKey };\n\n const mock = mockOf(env);\n if (mock) await start(mock, settings);\n else\n await start(realAmplitude(), settings).catch((error: unknown) => {\n // The SDK answers a second init after a failed one as done without\n // starting: the next attempt gets a fresh client.\n real = undefined;\n throw error;\n });\n\n const _state: RuntimeState = { lastIdentity: {} };\n return { ...config, settings: { ...settings, _state } };\n },\n\n async push(event, { config, rule, env, data, collector, logger }) {\n const settings: Settings = { apiKey: '', ...config.settings };\n const mappingSettings: Mapping = rule?.settings || {};\n const mock = mockOf(env);\n await (mock\n ? pushTo(\n mock,\n event,\n settings,\n mappingSettings,\n rule,\n data,\n collector,\n logger,\n )\n : pushTo(\n realAmplitude(),\n event,\n settings,\n mappingSettings,\n rule,\n data,\n collector,\n logger,\n ));\n },\n\n // The destination's required consent decides, as it gates the pushes: a\n // revocation opts out of Amplitude, a grant opts back in.\n async on(type, { config, data, env, collector }) {\n const required = config.consent;\n if (type !== 'consent' || !required || !isObject(data)) return;\n if (!Object.keys(required).some((key) => key in data)) return;\n\n const granted = getGrantedConsent(required, collector.consent) !== false;\n const amp = mockOf(env) ?? realAmplitude();\n await stepCall(\n () => amp.setOptOut(!granted),\n 'consent-not-applied',\n 'Amplitude did not take the consent update',\n );\n },\n};\n\nexport default destinationAmplitude;\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n} from '@walkeros/core';\nimport type { DestinationWeb } from '@walkeros/web-core';\nimport type { Types as AmplitudeTypes } from '@amplitude/unified';\nimport type { SessionReplayOptions } from '@amplitude/plugin-session-replay-browser';\nimport type { ExperimentPluginConfig } from '@amplitude/plugin-experiment-browser';\nimport type { InitOptions as EngagementInitOptions } from '@amplitude/engagement-browser';\n\n// Amplitude's BrowserOptions has an `identify` field that conflicts with our\n// walkerOS mapping-based `identify`. Omit it so Settings can re-declare it.\ntype BrowserOptions = Omit<AmplitudeTypes.BrowserOptions, 'identify'>;\nexport type AmplitudeBrowserOptions = AmplitudeTypes.BrowserOptions;\nexport type AmplitudePlugin = AmplitudeTypes.Plugin;\n\n/**\n * Destination-level settings.\n *\n * All Amplitude `BrowserOptions` are passthrough init options — extending the\n * SDK type directly keeps IntelliSense complete and prevents drift. The\n * destination adds:\n * - `apiKey` (required) — first arg to `amplitude.init(...)`\n * - `identify` — destination-level identity mapping\n * - `sessionReplay` / `experiment` / `engagement` — optional plugin configs\n * - `_state` — runtime state (not user-facing, mutated by init/push)\n */\nexport interface Settings extends BrowserOptions {\n apiKey: string;\n identify?: WalkerOSMapping.Value;\n sessionReplay?: SessionReplayOptions;\n experiment?: ExperimentPluginConfig & { deploymentKey: string };\n engagement?: boolean | EngagementInitOptions;\n /** Runtime state — populated by init() and mutated by push(). Not user-facing. */\n _state?: RuntimeState;\n}\n\nexport interface RuntimeState {\n /** Last-set identity values, used to skip redundant setUserId/setDeviceId/setSessionId calls. */\n lastIdentity?: {\n user?: string;\n device?: string;\n session?: number;\n };\n}\n\nexport type InitSettings = Partial<Settings>;\n\n/**\n * Per-rule mapping settings. Every feature here is a walkerOS mapping value\n * resolved via getMappingValue(). Revenue uniquely supports loop to produce\n * an array of items → N amplitude.revenue() calls.\n */\nexport interface Mapping {\n identify?: WalkerOSMapping.Value;\n revenue?: WalkerOSMapping.Value;\n group?: WalkerOSMapping.Value;\n groupIdentify?: WalkerOSMapping.Value;\n reset?: WalkerOSMapping.Value | boolean;\n}\n\n/**\n * Amplitude SDK surface: the subset of @amplitude/unified the\n * destination actually uses. Mirrors the real client's methods so tests can\n * mock each one via env.amplitude. Generic over the Identify and Revenue\n * instances, so the real SDK gets back the instances it created.\n */\nexport interface AmplitudeSDK<\n I extends IdentifyInstance = IdentifyInstance,\n R extends RevenueInstance = RevenueInstance,\n> {\n /** Analytics. */\n init: (apiKey: string, options?: AmplitudeBrowserOptions) => unknown;\n /** One plugin: Session Replay, Experiment or Guides & Surveys. */\n add: (plugin: AmplitudePlugin) => unknown;\n track: (\n eventType: string,\n eventProperties?: Record<string, unknown>,\n ) => unknown;\n identify: (identify: I) => unknown;\n revenue: (revenue: R) => unknown;\n reset: () => unknown;\n setOptOut: (optOut: boolean) => unknown;\n setUserId: (userId: string | undefined) => unknown;\n setDeviceId: (deviceId: string) => unknown;\n setSessionId: (sessionId: number) => unknown;\n setGroup: (groupType: string, groupName: string | string[]) => unknown;\n groupIdentify: (groupType: string, groupName: string, identify: I) => unknown;\n Identify: new () => I;\n Revenue: new () => R;\n}\n\n/** Chainable Identify, as the @amplitude/unified 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, as the @amplitude/unified 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 * Env — optional SDK override. Production leaves this undefined and the\n * destination falls back to the real @amplitude/unified module\n * import. Tests provide a mock via env.amplitude = { ... }.\n */\nexport interface Env extends DestinationWeb.Env {\n amplitude?: AmplitudeSDK;\n}\n\nexport type Types = CoreDestination.Types<Settings, Mapping, Env, InitSettings>;\n\nexport type Destination = DestinationWeb.Destination<Types>;\nexport type Config = DestinationWeb.Config<Types>;\n\nexport interface AmplitudeDestination extends Destination {\n env?: Env;\n}\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n"],"mappings":"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","names":["send"]}
|