{"version":3,"sources":["../../../src/helpers/analytics/instance.ts","../../../src/helpers/analytics/middlewares/grouping.ts","../../../src/helpers/analytics/middlewares/identity.ts"],"sourcesContent":["import { Analytics as DefaultAnalytics } from 'analytics'\n\nimport { GroupingMiddlewarePlugin, IdentityMiddlewarePlugin } from './middlewares'\n\nimport type { AnyPayload, AnalyticsConfig, AnalyticsInstanceWithGroups, PageData } from './types'\n\n/**\n * Type-safe wrapper on top of \"[analytics](https://www.npmjs.com/package/analytics)\" npm package\n * with support of group method (Posthog inspired) to group users into cohorts\n *\n * @example Init analytics in your app with type-guards\n * import { Analytics } from '@open-condo/miniapp-utils/helpers/analytics'\n * import { isDebug } from '@open-condo/miniapp-utils/helpers/environment'\n *\n * type MyAppEvents = {\n *     'order_create': { orderId: string, itemIds: Array<string> }\n *     'user_register': { userId: string, utmSource?: string  }\n * }\n *\n * type MyUserData = {\n *     'name'?: string\n *     'age'?: number\n * }\n *\n * type MyAppGroups = 'organization' | 'country'\n *\n * export const analytics = new Analytics<MyAppEvents, MyUserData, MyAppGroups>({\n *     app: appName,\n *     version: revision,\n *     debug: isDebug(),\n * })\n *\n * @example Use initialized analytics in your-app\n * import { Router } from 'next/router'\n * import { useEffect } from 'react'\n *\n * import { isValidCondoUIMessage } from '@open-condo/ui/events'\n *\n * import { analytics } from '@/domains/common/utils/analytics'\n * import { useAuth } from '@/domains/user/utils/auth'\n *\n * import type { FC } from 'react'\n *\n * export const ResidentAppEventsHandler: FC = () => {\n *     const user = useAuth()\n *     const { activeResident } = useActiveResident()\n *\n *     // User change tracking\n *     useEffect(() => {\n *         if (user) {\n *             analytics.identify(user.id, { name: user.name, type: user.type })\n *         }\n *     }, [user])\n *\n *     // Page views tracking\n *     useEffect(() => {\n *         const handleRouteChange = () => analytics.pageView()\n *         Router.events.on('routeChangeComplete', handleRouteChange)\n *\n *         return () => {\n *             Router.events.off('routeChangeComplete', handleRouteChange)\n *         }\n *     }, [])\n *\n *     // Condo UI events tracking\n *     useEffect(() => {\n *         if (typeof window !== 'undefined') {\n *             const handleMessage = async (e: MessageEvent) => {\n *                 if (isValidCondoUIMessage(e)) {\n *                     const { params: { event, ...eventData } } = e.data\n *                     await analytics.trackUntyped(event, eventData)\n *                 }\n *\n *             }\n *\n *             window.addEventListener('message', handleMessage)\n *\n *             return () => {\n *                 window.removeEventListener('message', handleMessage)\n *             }\n *         }\n *     }, [])\n *\n *     return null\n * }\n */\nexport class Analytics<\n    Events extends Record<string, AnyPayload> = Record<string, never>,\n    UserData extends AnyPayload = Record<string, never>,\n    GroupNames extends string = never,\n> {\n    private readonly _analytics: AnalyticsInstanceWithGroups<GroupNames>\n    private readonly _groups = new Set<GroupNames>()\n\n    constructor (config: AnalyticsConfig) {\n        this._analytics = DefaultAnalytics({\n            ...config,\n            plugins: [\n                IdentityMiddlewarePlugin,\n                GroupingMiddlewarePlugin,\n                ...(config.plugins || []),\n            ],\n        }) as AnalyticsInstanceWithGroups<GroupNames>\n        this._analytics.groups = this._groups\n    }\n\n    /**\n     * Tracks type-safe business events. Recommended to use in most cases in app's codebase.\n     * To add an event, modify \"Events\" generic.\n     */\n    async track<EventName extends Extract<keyof Events, string>>(eventName: EventName, eventData: Events[EventName]): Promise<void> {\n        await this._analytics.track(eventName, eventData)\n    }\n\n    /**\n     * Tracks untyped analytics events, used mainly for external sources (bridge / ui-kit / messages, etc.)\n     * @deprecated It's not recommended to use this in your business logic, consider using typed \"track\" instead\n     */\n    async trackUntyped (eventName: string, eventData: AnyPayload): Promise<void> {\n        await this._analytics.track(eventName, eventData)\n    }\n\n    /**\n     * Tracks page changing in SPAs\n     */\n    async pageView (data?: PageData): Promise<void> {\n        await this._analytics.page(data)\n    }\n\n    /**\n     * Identifies user in analytics provider.\n     * To specify all possible shape of user's data, modify \"UserData\" generic\n     *\n     * NOTE: Analytics plugins don't have a fixed behavior on how to handle consecutive identify calls.\n     * Some of them affect only subsequent events, others affect all user events.\n     * Therefore, it is not recommended to put cohort-specific data (organization, address, language, etc.) here.\n     * Instead, use something like \"group\" method if your plugins supports it.\n     */\n    async identify<Key extends keyof UserData> (userId: string, userData?: Pick<UserData, Key>): Promise<void> {\n        await this._analytics.identify(userId, userData)\n    }\n\n    /**\n     * Resets analytics providers\n     */\n    async reset (): Promise<void> {\n        for (const groupName of this._groups) {\n            const groupKey = Analytics.getGroupKey(groupName)\n            this._analytics.storage.removeItem(groupKey)\n        }\n        this._groups.clear()\n        await this._analytics.reset()\n    }\n\n    static getGroupKey (groupName: string): string {\n        return ['analytics', 'groups', groupName].join(':')\n    }\n\n    /**\n     * Associates the user with a group, adding the attributes `groups.${groupName} = groupId`\n     * to all subsequent analytic queries for the user\n     * @example\n     * analytics.setGroup('organization', organizationId)\n     */\n    setGroup (groupName: GroupNames, groupId: string): void {\n        const groupKey = Analytics.getGroupKey(groupName)\n        this._groups.add(groupName)\n        this._analytics.storage.setItem(groupKey, groupId)\n    }\n\n    /**\n     * Removes the current user from the group, stripping the “groups.${groupName}”\n     * attribute from all subsequent eventualities\n     * @example\n     * deleteOrganization()\n     *     .then(() => analytics.removeGroup('organization'))\n     */\n    removeGroup (groupName: GroupNames): void {\n        const groupKey = Analytics.getGroupKey(groupName)\n        this._analytics.storage.removeItem(groupKey)\n        this._groups.delete(groupName)\n    }\n}\n","import { Analytics } from '../instance'\n\nimport type { AnalyticsPlugin, PluginTrackData } from '../types'\n\nfunction _addGroupingProperties (data: PluginTrackData): PluginTrackData {\n    const { instance } = data\n\n    for (const groupName of instance.groups) {\n        const groupKey = Analytics.getGroupKey(groupName)\n        const groupValue = instance.storage.getItem(groupKey)\n\n        if (typeof groupValue === 'string') {\n            const groupAttrName = `groups.${groupName}`\n            data.payload.properties[groupAttrName] = groupValue\n        }\n    }\n    return data\n}\n\nexport const GroupingMiddlewarePlugin: AnalyticsPlugin = {\n    name: 'analytics-plugin-grouping',\n    track: _addGroupingProperties,\n    page: _addGroupingProperties,\n}","import type { AnalyticsPlugin, PluginTrackData } from '../types'\n\nconst IDENTITY_PROPERTIES = ['app', 'version']\n\nfunction _addIdentityProperties (data: PluginTrackData): PluginTrackData {\n    const { instance } = data\n\n    for (const contextPropertyName of IDENTITY_PROPERTIES) {\n        const propertyValue = instance.getState(`context.${contextPropertyName}`)\n        if (typeof propertyValue === 'string') {\n            data.payload.properties[contextPropertyName] = propertyValue\n        }\n    }\n\n    return data\n}\n\nexport const IdentityMiddlewarePlugin: AnalyticsPlugin = {\n    name: 'analytics-plugin-identity',\n    track: _addIdentityProperties,\n    page: _addIdentityProperties,\n}"],"mappings":";AAAA,SAAS,aAAa,wBAAwB;;;ACI9C,SAAS,uBAAwB,MAAwC;AACrE,QAAM,EAAE,SAAS,IAAI;AAErB,aAAW,aAAa,SAAS,QAAQ;AACrC,UAAM,WAAW,UAAU,YAAY,SAAS;AAChD,UAAM,aAAa,SAAS,QAAQ,QAAQ,QAAQ;AAEpD,QAAI,OAAO,eAAe,UAAU;AAChC,YAAM,gBAAgB,UAAU,SAAS;AACzC,WAAK,QAAQ,WAAW,aAAa,IAAI;AAAA,IAC7C;AAAA,EACJ;AACA,SAAO;AACX;AAEO,IAAM,2BAA4C;AAAA,EACrD,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AACV;;;ACrBA,IAAM,sBAAsB,CAAC,OAAO,SAAS;AAE7C,SAAS,uBAAwB,MAAwC;AACrE,QAAM,EAAE,SAAS,IAAI;AAErB,aAAW,uBAAuB,qBAAqB;AACnD,UAAM,gBAAgB,SAAS,SAAS,WAAW,mBAAmB,EAAE;AACxE,QAAI,OAAO,kBAAkB,UAAU;AACnC,WAAK,QAAQ,WAAW,mBAAmB,IAAI;AAAA,IACnD;AAAA,EACJ;AAEA,SAAO;AACX;AAEO,IAAM,2BAA4C;AAAA,EACrD,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AACV;;;AFiEO,IAAM,YAAN,MAAM,WAIX;AAAA,EAIE,YAAa,QAAyB;AAFtC,SAAiB,UAAU,oBAAI,IAAgB;AAG3C,SAAK,aAAa,iBAAiB;AAAA,MAC/B,GAAG;AAAA,MACH,SAAS;AAAA,QACL;AAAA,QACA;AAAA,QACA,GAAI,OAAO,WAAW,CAAC;AAAA,MAC3B;AAAA,IACJ,CAAC;AACD,SAAK,WAAW,SAAS,KAAK;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MAAuD,WAAsB,WAA6C;AAC5H,UAAM,KAAK,WAAW,MAAM,WAAW,SAAS;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aAAc,WAAmB,WAAsC;AACzE,UAAM,KAAK,WAAW,MAAM,WAAW,SAAS;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAU,MAAgC;AAC5C,UAAM,KAAK,WAAW,KAAK,IAAI;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,SAAsC,QAAgB,UAA+C;AACvG,UAAM,KAAK,WAAW,SAAS,QAAQ,QAAQ;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAwB;AAC1B,eAAW,aAAa,KAAK,SAAS;AAClC,YAAM,WAAW,WAAU,YAAY,SAAS;AAChD,WAAK,WAAW,QAAQ,WAAW,QAAQ;AAAA,IAC/C;AACA,SAAK,QAAQ,MAAM;AACnB,UAAM,KAAK,WAAW,MAAM;AAAA,EAChC;AAAA,EAEA,OAAO,YAAa,WAA2B;AAC3C,WAAO,CAAC,aAAa,UAAU,SAAS,EAAE,KAAK,GAAG;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,SAAU,WAAuB,SAAuB;AACpD,UAAM,WAAW,WAAU,YAAY,SAAS;AAChD,SAAK,QAAQ,IAAI,SAAS;AAC1B,SAAK,WAAW,QAAQ,QAAQ,UAAU,OAAO;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,YAAa,WAA6B;AACtC,UAAM,WAAW,WAAU,YAAY,SAAS;AAChD,SAAK,WAAW,QAAQ,WAAW,QAAQ;AAC3C,SAAK,QAAQ,OAAO,SAAS;AAAA,EACjC;AACJ;","names":[]}