{"version":3,"sources":["../src/helpers/analytics/instance.ts","../src/helpers/analytics/middlewares/grouping.ts","../src/helpers/analytics/middlewares/identity.ts","../src/helpers/apollo.ts","../src/helpers/proxying/utils.ts","../src/helpers/ip/utils.ts","../src/helpers/ip/ipv4.ts","../src/helpers/ip/ipv6.ts","../src/helpers/ip/index.ts","../src/helpers/proxying/proxy.ts","../src/helpers/sender.ts","../src/helpers/embeddingContext.tsx","../src/helpers/uuid.ts","../src/helpers/tracing.ts","../src/helpers/collections.ts","../src/helpers/cookies.ts","../src/helpers/environment.ts","../src/helpers/posthog.ts","../src/hooks/useSetPageActionsHandlers.ts","../src/hooks/useEffectOnce.ts","../src/hooks/useIntersectionObserver.ts","../src/hooks/usePrevious.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}","import { serialize as serializeCookie } from 'cookie'\nimport { setCookie, getCookies } from 'cookies-next'\n\nimport { getProxyHeadersForIp } from './proxying'\nimport { getRequestIp } from './proxying'\nimport {\n    FINGERPRINT_ID_COOKIE_NAME,\n    generateFingerprint,\n} from './sender'\nimport { getAppTracingHeaders } from './tracing'\n\nimport type { DefaultContext, RequestHandler } from '@apollo/client'\nimport type { IncomingMessage, ServerResponse } from 'http'\n\ntype Response = ServerResponse\n\ntype SSRContext = {\n    headers: Record<string, string>\n    defaultContext: DefaultContext\n}\n\nexport type TracingMiddlewareOptions = {\n    serviceUrl: string\n    codeVersion: string\n    target?: string\n}\n\nexport type SSRProxyingMiddlewareOptions = {\n    apiUrl: string\n    proxyId?: string\n    proxySecret?: string\n}\n\nexport function getTracingMiddleware (options: TracingMiddlewareOptions): RequestHandler {\n    return function (operation, forward) {\n        operation.setContext((previousContext: DefaultContext) => {\n            const { headers: previousHeaders } = previousContext\n\n            return {\n                ...previousContext,\n                headers: getAppTracingHeaders({\n                    ...options,\n                    previousHeaders,\n                }),\n            }\n        })\n\n        return forward(operation)\n    }\n}\n\nexport function getSSRProxyingMiddleware ({ proxyId, proxySecret, apiUrl }: SSRProxyingMiddlewareOptions): RequestHandler {\n    return function (operation, forward) {\n        operation.setContext((previousContext: DefaultContext) => {\n            if (typeof previousContext.clientIp !== 'string' || !proxyId || !proxySecret) return previousContext\n            const proxyHeaders = getProxyHeadersForIp(\n                'POST',\n                apiUrl,\n                previousContext.clientIp,\n                proxyId,\n                proxySecret,\n            )\n\n            return {\n                ...previousContext,\n                headers: {\n                    ...previousContext.headers,\n                    ...proxyHeaders,\n                },\n            }\n        })\n\n        return forward(operation)\n    }\n}\n\nexport function prepareSSRContext (req?: IncomingMessage, res?: Response): SSRContext {\n    if (!req) {\n        return {\n            headers: {},\n            defaultContext: {},\n        }\n    }\n\n    const requestCookies = getCookies({ req, res })\n\n    if (!requestCookies[FINGERPRINT_ID_COOKIE_NAME]) {\n        const fingerprint = generateFingerprint()\n        requestCookies[FINGERPRINT_ID_COOKIE_NAME] = fingerprint\n        // NOTE: req and res are used to operate \"set-cookie\" headers\n        setCookie(FINGERPRINT_ID_COOKIE_NAME, fingerprint, { req, res })\n    }\n\n    const cookieHeader = Object.entries(requestCookies)\n        .map(([name, value]) => value ? serializeCookie(name, value) : null)\n        .filter(Boolean)\n        .join(';')\n\n    const clientIp = getRequestIp(req, () => true)\n\n    const headers: Record<string, string> = {\n        'cookie': cookieHeader,\n    }\n\n    if (req.headers['accept-language']) {\n        headers['accept-language'] = req.headers['accept-language']\n    }\n\n    return {\n        headers,\n        defaultContext: {\n            clientIp,\n        },\n    }\n}\n","import jwt from 'jsonwebtoken'\nimport proxyAddr from 'proxy-addr'\nimport { z } from 'zod'\n\nimport { isInSubnet } from '../ip'\n\nimport type { IncomingMessage } from 'http'\n\ntype ProxyConfig = {\n    address: string | Array<string>\n    secret: string\n}\n\ntype ProxyId = string\n\nexport type KnownProxies = Record<ProxyId, ProxyConfig>\n\nexport type TrustProxyFunction = (proxyAddr: string, idx: number) => boolean\n\nconst _ipSchema = z.union([z.ipv4(), z.ipv6()])\nconst _timeStampBasicRegexp = /^\\d+$/\n\nconst DEFAULT_PROXY_TIMEOUT_IN_MS = 5_000 // 5 sec to pass request is enough\nconst X_PROXY_ID_HEADER = 'x-proxy-id' as const\nconst X_PROXY_IP_HEADER = 'x-proxy-ip' as const\nconst X_PROXY_TIMESTAMP_HEADER = 'x-proxy-timestamp' as const\nconst X_PROXY_SIGNATURE_HEADER = 'x-proxy-signature' as const\n\nexport type ProxyHeaders = {\n    [X_PROXY_ID_HEADER]: string\n    [X_PROXY_IP_HEADER]: string\n    [X_PROXY_TIMESTAMP_HEADER]: string\n    [X_PROXY_SIGNATURE_HEADER]: string\n}\n\nfunction _getTimestampFromHeader (timestamp: string) {\n    if (!_timeStampBasicRegexp.test(timestamp)) return Number.NaN\n    return (new Date(parseInt(timestamp))).getTime()\n}\n\nfunction _isProxyIP (ip: string, proxyConfig: KnownProxies[string]) {\n    const addresses = Array.isArray(proxyConfig.address) ? proxyConfig.address : [proxyConfig.address]\n    const config = addresses.reduce((acc, addr) => {\n        const isSubnet = addr.split('/').length > 1\n        if (isSubnet) {\n            acc.subnets.push(addr)\n        } else {\n            acc.ips.push(addr)\n        }\n\n        return acc\n    }, { ips: [] as Array<string>, subnets: [] as Array<string> })\n\n    if (config.ips.length && config.ips.includes(ip)) {\n        return true\n    }\n\n    return !!(config.subnets.length && isInSubnet(ip, config.subnets))\n}\n\nexport function getRequestIp (req: IncomingMessage, trustProxyFn: TrustProxyFunction, knownProxies?: KnownProxies): string {\n    // NOTE: That's what express does under the hood: https://github.com/expressjs/express/blob/4.x/lib/request.js#L349\n    const originalIP = proxyAddr(req, trustProxyFn)\n\n    if (!knownProxies) return originalIP\n\n    const xProxyId = req.headers[X_PROXY_ID_HEADER]\n    const xProxyIp = req.headers[X_PROXY_IP_HEADER]\n    // NOTE: used to prevent relay attacks\n    const xProxyTimestamp = req.headers[X_PROXY_TIMESTAMP_HEADER]\n    const xProxySignature = req.headers[X_PROXY_SIGNATURE_HEADER]\n\n    if (\n        typeof xProxyId !== 'string' ||\n        typeof xProxyIp !== 'string' ||\n        typeof xProxyTimestamp !== 'string' ||\n        typeof xProxySignature !== 'string'\n    ) {\n        return originalIP\n    }\n\n    // NOTE: validate, that x-proxy-ip is correct IP\n    const { success: isValidIp } = _ipSchema.safeParse(xProxyIp)\n    if (!isValidIp) {\n        return originalIP\n    }\n\n    // NOTE: validate timestamp: it should less than now and no more than 5s less than now (recent enough)\n    const timestamp = _getTimestampFromHeader(xProxyTimestamp)\n    const now = Date.now()\n    if (\n        Number.isNaN(timestamp) ||\n        timestamp > now ||\n        now - timestamp > DEFAULT_PROXY_TIMEOUT_IN_MS\n    ) {\n        return originalIP\n    }\n\n    // NOTE: validate signature and proxy IP\n    if (!Object.hasOwn(knownProxies, xProxyId)) {\n        return originalIP\n    }\n    const proxyConfig = knownProxies[xProxyId]\n\n    if (!proxyConfig || !_isProxyIP(originalIP, proxyConfig)) {\n        return originalIP\n    }\n\n    try {\n        // NOTE: config is passed from outside, where its obtained from .env, so its not hard-coded\n        // nosemgrep: javascript.jsonwebtoken.security.jwt-hardcode.hardcoded-jwt-secret\n        const jwtPayload = jwt.verify(xProxySignature, proxyConfig.secret, { algorithms: ['HS256'] })\n        const expectedPayloadSchema = z.object({\n            [X_PROXY_TIMESTAMP_HEADER]: z.literal(xProxyTimestamp),\n            [X_PROXY_IP_HEADER]: z.literal(xProxyIp),\n            [X_PROXY_ID_HEADER]: z.literal(xProxyId),\n            method: z.literal(req.method),\n            url: z.literal(req.url),\n        })\n        const { success: isMatchingSignature } = expectedPayloadSchema.safeParse(jwtPayload)\n\n        return isMatchingSignature ? xProxyIp : originalIP\n    } catch {\n        return originalIP\n    }\n}\n\nexport function getProxyHeadersForIp (method: string, url: string, ip: string, proxyId: string, secret: string): ProxyHeaders {\n    const timestampString = String(Date.now())\n\n    return {\n        [X_PROXY_IP_HEADER]: ip,\n        [X_PROXY_ID_HEADER]: proxyId,\n        [X_PROXY_TIMESTAMP_HEADER]: timestampString,\n        [X_PROXY_SIGNATURE_HEADER]: jwt.sign({\n            [X_PROXY_IP_HEADER]: ip,\n            [X_PROXY_ID_HEADER]: proxyId,\n            [X_PROXY_TIMESTAMP_HEADER]: timestampString,\n            method,\n            url,\n        }, secret, {\n            expiresIn: Math.round(DEFAULT_PROXY_TIMEOUT_IN_MS / 1000),\n            algorithm: 'HS256',\n        }),\n    }\n}\n\nexport function isRelativeUrl (url: string) {\n    return url.startsWith('/')\n}\n\nexport function replaceUpstreamEndpoint ({\n    endpoint,\n    proxyPrefix,\n    upstreamPrefix,\n    upstreamOrigin,\n    rewrites = {},\n}: {\n    endpoint: string\n    proxyPrefix: string\n    upstreamPrefix: string\n    upstreamOrigin: string\n    rewrites?: Record<string, string>\n}) {\n    const isRelativeLocation = isRelativeUrl(endpoint)\n    const locationUrl = new URL(endpoint, 'https://_')\n\n    let targetLocation\n\n    const lookupUrl = new URL(endpoint, upstreamOrigin)\n    lookupUrl.search = ''\n    // First lookup relative location ('/some/path')\n    if (isRelativeLocation || lookupUrl.origin === upstreamOrigin) {\n        targetLocation ??= rewrites[lookupUrl.pathname]\n    }\n\n    // Then lookup absolute location ('https://upstreamhost.com/some/path')\n    targetLocation ??= rewrites[lookupUrl.toString()]\n\n    // If found lookup, perform smart replacement\n    if (targetLocation) {\n        const isRelativeTarget = isRelativeUrl(targetLocation)\n        const targetUrl = new URL(targetLocation, upstreamOrigin)\n        const targetSearchParams = new URLSearchParams(targetUrl.searchParams)\n        // Replace target search params with location search params, then apply target search params on top\n        if (!targetUrl.hash) {\n            targetUrl.hash = locationUrl.hash\n        }\n        targetUrl.search = locationUrl.search\n        for (const [name] of targetSearchParams.entries()) {\n            targetUrl.searchParams.delete(name)\n        }\n        for (const [name, value] of targetSearchParams.entries()) {\n            targetUrl.searchParams.append(name, value)\n        }\n\n        if (isRelativeTarget) {\n            return targetUrl.pathname + targetUrl.search + targetUrl.hash\n        } else {\n            return targetUrl.toString()\n        }\n    }\n\n    // If location is relative or has same as upstream domain, try to replace back upstreamPrefix with proxyPrefix\n    if ((isRelativeLocation || locationUrl.origin === upstreamOrigin) && locationUrl.pathname.startsWith(upstreamPrefix)) {\n        locationUrl.pathname = proxyPrefix + locationUrl.pathname.slice(upstreamPrefix.length)\n\n        return locationUrl.pathname + locationUrl.search + locationUrl.hash\n    }\n\n    return endpoint\n}\n","// RegExp for testing if a string represents an IPv4 address\nconst v4Seg = '(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'\nconst v4Str = `(${v4Seg}[.]){3}${v4Seg}`\nconst IPv4Reg = new RegExp(`^${v4Str}$`)\n\n// RegExp for testing if a string represents an IPv6 address\nconst v6Seg = '(?:[0-9a-fA-F]{1,4})'\nconst IPv6Reg = new RegExp(\n    '^(' +\n    `(?:${v6Seg}:){7}(?:${v6Seg}|:)|` +\n    `(?:${v6Seg}:){6}(?:${v4Str}|:${v6Seg}|:)|` +\n    `(?:${v6Seg}:){5}(?::${v4Str}|(:${v6Seg}){1,2}|:)|` +\n    `(?:${v6Seg}:){4}(?:(:${v6Seg}){0,1}:${v4Str}|(:${v6Seg}){1,3}|:)|` +\n    `(?:${v6Seg}:){3}(?:(:${v6Seg}){0,2}:${v4Str}|(:${v6Seg}){1,4}|:)|` +\n    `(?:${v6Seg}:){2}(?:(:${v6Seg}){0,3}:${v4Str}|(:${v6Seg}){1,5}|:)|` +\n    `(?:${v6Seg}:){1}(?:(:${v6Seg}){0,4}:${v4Str}|(:${v6Seg}){1,6}|:)|` +\n    `(?::((?::${v6Seg}){0,5}:${v4Str}|(?::${v6Seg}){1,7}|:))` +\n    ')(%[0-9a-zA-Z]{1,})?$'\n)\n\n/**\n * Returns true if the string represents an IPv4 address. Matches Node.js net.isIPv4\n * functionality.\n */\nexport function isIPv4 (s: string) {\n    return IPv4Reg.test(s)\n}\n\n/**\n * Returns true if the string represents an IPv6 address. Matches Node.js net.isIPv6\n * functionality.\n */\nexport function isIPv6 (s: string) {\n    return IPv6Reg.test(s)\n}\n\nexport function isIP (s: string) {\n    if (isIPv4(s)) return 4\n    if (isIPv6(s)) return 6\n    return 0\n}\n","import ipRanges from './ranges'\nimport * as util from './utils'\n\n/**\n * Given an IPv4 address, convert it to a 32-bit long integer.\n * @param ip the IPv4 address to expand\n * @throws if the string is not a valid IPv4 address\n */\nfunction ipv4ToLong (ip: string) {\n    if (!util.isIPv4(ip)) {\n        throw new Error(`not a valid IPv4 address: ${ip}`)\n    }\n    const octets = ip.split('.')\n    return (\n        ((parseInt(octets[0], 10) << 24) +\n            (parseInt(octets[1], 10) << 16) +\n            (parseInt(octets[2], 10) << 8) +\n            parseInt(octets[3], 10)) >>>\n        0\n    )\n}\n\n// this is the most optimised checker.\nfunction createLongChecker (subnet: string): (addressLong: number) => boolean {\n    const [subnetAddress, prefixLengthString] = subnet.split('/')\n    const prefixLength = parseInt(prefixLengthString, 10)\n    if (!subnetAddress || !Number.isInteger(prefixLength)) {\n        throw new Error(`not a valid IPv4 subnet: ${subnet}`)\n    }\n\n    if (prefixLength < 0 || prefixLength > 32) {\n        throw new Error(`not a valid IPv4 prefix length: ${prefixLength} (from ${subnet})`)\n    }\n\n    const subnetLong = ipv4ToLong(subnetAddress)\n    return addressLong => {\n        if (prefixLength === 0) {\n            return true\n        }\n        const subnetPrefix = subnetLong >> (32 - prefixLength)\n        const addressPrefix = addressLong >> (32 - prefixLength)\n\n        return subnetPrefix === addressPrefix\n    }\n}\n\n/**\n * The functional version, creates a checking function that takes an IPv4 Address and\n * returns whether or not it is contained in (one of) the subnet(s).\n * @param subnetOrSubnets the IPv4 CIDR to test (or an array of them)\n * @throws if the subnet is not a valid IP addresses, or the CIDR prefix length\n *  is not valid\n */\nexport function createChecker (\n    subnetOrSubnets: string | string[]\n): (address: string) => boolean {\n    if (Array.isArray(subnetOrSubnets)) {\n        const checks = subnetOrSubnets.map(subnet => createLongChecker(subnet))\n        return address => {\n            const addressLong = ipv4ToLong(address)\n            return checks.some(check => check(addressLong))\n        }\n    }\n    const check = createLongChecker(subnetOrSubnets)\n    return address => {\n        const addressLong = ipv4ToLong(address)\n        return check(addressLong)\n    }\n}\n\n/**\n * Test if the given IPv4 address is contained in the specified subnet.\n * @param address the IPv4 address to check\n * @param subnetOrSubnets the IPv4 CIDR to test (or an array of them)\n * @throws if the address or subnet are not valid IP addresses, or the CIDR prefix length\n *  is not valid\n */\nexport function isInSubnet (address: string, subnetOrSubnets: string | string[]): boolean {\n    return createChecker(subnetOrSubnets)(address)\n}\n\n// cache these special subnet checkers\nconst specialNetsCache: Record<string, (address: string) => boolean> = {}\n\n/** Test if the given IP address is a private/internal IP address. */\nexport function isPrivate (address: string) {\n    if (!('private' in specialNetsCache)) {\n        specialNetsCache['private'] = createChecker(ipRanges.private.ipv4)\n    }\n    return specialNetsCache['private'](address)\n}\n\n/** Test if the given IP address is a localhost address. */\nexport function isLocalhost (address: string) {\n    if (!('localhost' in specialNetsCache)) {\n        specialNetsCache['localhost'] = createChecker(ipRanges.localhost.ipv4)\n    }\n    return specialNetsCache['localhost'](address)\n}\n\n/** Test if the given IP address is in a known reserved range and not a normal host IP */\nexport function isReserved (address: string) {\n    if (!('reserved' in specialNetsCache)) {\n        specialNetsCache['reserved'] = createChecker(ipRanges.reserved.ipv4)\n    }\n    return specialNetsCache['reserved'](address)\n}\n\n/**\n * Test if the given IP address is a special address of any kind (private, reserved,\n * localhost)\n */\nexport function isSpecial (address: string) {\n    if (!('special' in specialNetsCache)) {\n        specialNetsCache['special'] = createChecker([\n            ...ipRanges.private.ipv4,\n            ...ipRanges.localhost.ipv4,\n            ...ipRanges.reserved.ipv4,\n        ])\n    }\n    return specialNetsCache['special'](address)\n}","import ipRanges from './ranges'\nimport * as util from './utils'\n\n// Note: Profiling shows that on recent versions of Node, string.split(RegExp) is faster\n// than string.split(string).\nconst dot = /\\./\nconst mappedIpv4 = /^(.+:ffff:)(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})(?:%.+)?$/\nconst colon = /:/\nconst doubleColon = /::/\n\n/**\n * Given a mapped IPv4 address (e.g. ::ffff:203.0.113.38 or similar), convert it to the\n * equivalent standard IPv6 address.\n * @param ip the IPv4-to-IPv6 mapped address\n */\nfunction mappedIpv4ToIpv6 (ip: string) {\n    const matches = ip.match(mappedIpv4)\n\n    if (!matches || !util.isIPv4(matches[2])) {\n        throw new Error(`not a mapped IPv4 address: ${ip}`)\n    }\n\n    // mapped IPv4 address\n    const prefix = matches[1]\n    const ipv4 = matches[2]\n\n    const parts = ipv4.split(dot).map(x => parseInt(x, 10))\n\n    const segment7 = ((parts[0] << 8) + parts[1]).toString(16)\n    const segment8 = ((parts[2] << 8) + parts[3]).toString(16)\n\n    return `${prefix}${segment7}:${segment8}`\n}\n\n/**\n * Given a mapped IPv4 address, return the bare IPv4 equivalent.\n */\nexport function extractMappedIpv4 (ip: string) {\n    const matches = ip.match(mappedIpv4)\n\n    if (!matches || !util.isIPv4(matches[2])) {\n        throw new Error(`not a mapped IPv4 address: ${ip}`)\n    }\n\n    return matches[2]\n}\n\n/**\n * Given an IP address that may have double-colons, expand all segments and return them\n * as an array of 8 segments (16-bit words). As a peformance enhancement (indicated by\n * profiling), for any segment that was missing but should be a '0', returns undefined.\n * @param ip the IPv6 address to expand\n * @throws if the string is not a valid IPv6 address\n */\nfunction getIpv6Segments (ip: string): string[] {\n    if (!util.isIPv6(ip)) {\n        throw new Error(`not a valid IPv6 address: ${ip}`)\n    }\n\n    if (dot.test(ip)) {\n        return getIpv6Segments(mappedIpv4ToIpv6(ip))\n    }\n\n    // break it into an array, including missing \"::\" segments\n    const [beforeChunk, afterChunk] = ip.split(doubleColon)\n\n    const beforeParts = (beforeChunk && beforeChunk.split(colon)) || []\n    const afterParts = (afterChunk && afterChunk.split(colon)) || []\n    const missingSegments = new Array<string>(8 - (beforeParts.length + afterParts.length))\n\n    return beforeParts.concat(missingSegments, afterParts)\n}\n\n/**\n * Test if the given IPv6 address is contained in the specified subnet.\n * @param address the IPv6 address to check\n * @param subnetOrSubnets the IPv6 CIDR to test (or an array of them)\n * @throws if the address or subnet are not valid IP addresses, or the CIDR prefix length\n *  is not valid\n */\nexport function isInSubnet (address: string, subnetOrSubnets: string | string[]): boolean {\n    return createChecker(subnetOrSubnets)(address)\n}\n\n/**\n * Create a function to test if a given IPv6 address is contained in the specified subnet.\n * @param subnetOrSubnets the IPv6 CIDR to test (or an array of them)\n * @throws if the subnet(s) are not valid IP addresses, or the CIDR prefix lengths\n *  are not valid\n */\nexport function createChecker (\n    subnetOrSubnets: string | string[]\n): (address: string) => boolean {\n    if (Array.isArray(subnetOrSubnets)) {\n        const checks = subnetOrSubnets.map(subnet => createSegmentChecker(subnet))\n        return address => {\n            const segments = getIpv6Segments(address)\n            return checks.some(check => check(segments))\n        }\n    }\n    const check = createSegmentChecker(subnetOrSubnets)\n    return address => {\n        const segments = getIpv6Segments(address)\n        return check(segments)\n    }\n}\n\n// This creates the last function that works on the most deconstructed data\nfunction createSegmentChecker (subnet: string): (segments: string[]) => boolean {\n    const [subnetAddress, prefixLengthString] = subnet.split('/')\n    const prefixLength = parseInt(prefixLengthString, 10)\n\n    if (!subnetAddress || !Number.isInteger(prefixLength)) {\n        throw new Error(`not a valid IPv6 CIDR subnet: ${subnet}`)\n    }\n\n    if (prefixLength < 0 || prefixLength > 128) {\n        throw new Error(`not a valid IPv6 prefix length: ${prefixLength} (from ${subnet})`)\n    }\n\n    // the next line throws if the address is not a valid IPv6 address\n    const subnetSegments = getIpv6Segments(subnetAddress)\n\n    return addressSegments => {\n        for (let i = 0; i < 8; ++i) {\n            const bitCount = Math.min(prefixLength - i * 16, 16)\n\n            if (bitCount <= 0) {\n                break\n            }\n\n            const subnetPrefix =\n                ((subnetSegments[i] && parseInt(subnetSegments[i], 16)) || 0) >> (16 - bitCount)\n\n            const addressPrefix =\n                ((addressSegments[i] && parseInt(addressSegments[i], 16)) || 0) >>\n                (16 - bitCount)\n\n            if (subnetPrefix !== addressPrefix) {\n                return false\n            }\n        }\n\n        return true\n    }\n}\n\n// cache these special subnet checkers\nconst specialNetsCache: Record<string, (address: string) => boolean> = {}\n\n/** Test if the given IP address is a private/internal IP address. */\nexport function isPrivate (address: string) {\n    if (!('private' in specialNetsCache)) {\n        specialNetsCache['private'] = createChecker(ipRanges.private.ipv6)\n    }\n    return specialNetsCache['private'](address)\n}\n\n/** Test if the given IP address is a localhost address. */\nexport function isLocalhost (address: string) {\n    if (!('localhost' in specialNetsCache)) {\n        specialNetsCache['localhost'] = createChecker(ipRanges.localhost.ipv6)\n    }\n    return specialNetsCache['localhost'](address)\n}\n\n/** Test if the given IP address is an IPv4 address mapped onto IPv6 */\nexport function isIPv4MappedAddress (address: string) {\n    if (!('mapped' in specialNetsCache)) {\n        specialNetsCache['mapped'] = createChecker('::ffff:0:0/96')\n    }\n    if (specialNetsCache['mapped'](address)) {\n        const matches = address.match(mappedIpv4)\n        return Boolean(matches && util.isIPv4(matches[2]))\n    }\n    return false\n}\n\n/** Test if the given IP address is in a known reserved range and not a normal host IP */\nexport function isReserved (address: string) {\n    if (!('reserved' in specialNetsCache)) {\n        specialNetsCache['reserved'] = createChecker(ipRanges.reserved.ipv6)\n    }\n    return specialNetsCache['reserved'](address)\n}\n\n/**\n * Test if the given IP address is a special address of any kind (private, reserved,\n * localhost)\n */\nexport function isSpecial (address: string) {\n    if (!('special' in specialNetsCache)) {\n        specialNetsCache['special'] = createChecker([\n            ...ipRanges.private.ipv6,\n            ...ipRanges.localhost.ipv6,\n            ...ipRanges.reserved.ipv6,\n        ])\n    }\n    return specialNetsCache['special'](address)\n}","import * as IPv4 from './ipv4'\nimport * as IPv6 from './ipv6'\nimport * as util from './utils'\n\nexport { isIP, isIPv4, isIPv6 } from './utils'\nexport { IPv4, IPv6 }\n\n/**\n * Test if the given IP address is contained in the specified subnet.\n * @param address the IPv4 or IPv6 address to check\n * @param subnetOrSubnets the IPv4 or IPv6 CIDR to test (or an array of them)\n * @throws if any of the address or subnet(s) are not valid IP addresses, or the CIDR\n *  prefix length is not valid\n */\nexport function isInSubnet (address: string, subnetOrSubnets: string | string[]): boolean {\n    return createChecker(subnetOrSubnets)(address)\n}\n/**\n * Create a function to test if the given IP address is contained in the specified subnet.\n * @param subnetOrSubnets the IPv4 or IPv6 CIDR to test (or an array of them)\n * @throws if any of the subnet(s) are not valid IP addresses, or the CIDR\n *  prefix length is not valid\n */\nexport function createChecker (\n    subnetOrSubnets: string | string[]\n): (address: string) => boolean {\n    if (!Array.isArray(subnetOrSubnets)) {\n        return createChecker([subnetOrSubnets])\n    }\n\n    const subnetsByVersion = subnetOrSubnets.reduce(\n        (acc, subnet) => {\n            const ip = subnet.split('/')[0];\n            (acc[util.isIP(ip)] as string[]).push(subnet)\n            return acc\n        },\n        { 0: [], 4: [], 6: [] }\n    )\n\n    if (subnetsByVersion[0].length !== 0) {\n        throw new Error(`some subnets are not valid IP addresses: ${subnetsByVersion[0]}`)\n    }\n\n    const check4 = IPv4.createChecker(subnetsByVersion[4])\n    const check6 = IPv6.createChecker(subnetsByVersion[6])\n\n    return address => {\n        if (!util.isIP(address)) {\n            throw new Error(`not a valid IPv4 or IPv6 address: ${address}`)\n        }\n\n        // for mapped IPv4 addresses, compare against both IPv6 and IPv4 subnets\n        if (util.isIPv6(address) && IPv6.isIPv4MappedAddress(address)) {\n            return check6(address) || check4(IPv6.extractMappedIpv4(address))\n        }\n\n        if (util.isIPv6(address)) {\n            return check6(address)\n        } else {\n            return check4(address)\n        }\n    }\n}\n\n/** Test if the given IP address is a private/internal IP address. */\nexport function isPrivate (address: string) {\n    if (util.isIPv6(address)) {\n        if (IPv6.isIPv4MappedAddress(address)) {\n            return IPv4.isPrivate(IPv6.extractMappedIpv4(address))\n        }\n        return IPv6.isPrivate(address)\n    } else {\n        return IPv4.isPrivate(address)\n    }\n}\n\n/** Test if the given IP address is a localhost address. */\nexport function isLocalhost (address: string) {\n    if (util.isIPv6(address)) {\n        if (IPv6.isIPv4MappedAddress(address)) {\n            return IPv4.isLocalhost(IPv6.extractMappedIpv4(address))\n        }\n        return IPv6.isLocalhost(address)\n    } else {\n        return IPv4.isLocalhost(address)\n    }\n}\n\n/** Test if the given IP address is an IPv4 address mapped onto IPv6 */\nexport function isIPv4MappedAddress (address: string) {\n    if (util.isIPv6(address)) {\n        return IPv6.isIPv4MappedAddress(address)\n    } else {\n        return false\n    }\n}\n\n/** Test if the given IP address is in a known reserved range and not a normal host IP */\nexport function isReserved (address: string) {\n    if (util.isIPv6(address)) {\n        if (IPv6.isIPv4MappedAddress(address)) {\n            return IPv4.isReserved(IPv6.extractMappedIpv4(address))\n        }\n        return IPv6.isReserved(address)\n    } else {\n        return IPv4.isReserved(address)\n    }\n}\n\n/**\n * Test if the given IP address is a special address of any kind (private, reserved,\n * localhost)\n */\nexport function isSpecial (address: string) {\n    if (util.isIPv6(address)) {\n        if (IPv6.isIPv4MappedAddress(address)) {\n            return IPv4.isSpecial(IPv6.extractMappedIpv4(address))\n        }\n        return IPv6.isSpecial(address)\n    } else {\n        return IPv4.isSpecial(address)\n    }\n}\n\nexport const check = isInSubnet","import httpProxy from 'http-proxy'\n\nimport { getProxyHeadersForIp, getRequestIp, replaceUpstreamEndpoint } from './utils'\n\nimport type { KnownProxies, TrustProxyFunction } from './utils'\nimport type { IncomingMessage, ServerResponse } from 'http'\n\ntype IpProxyingOptions = {\n    /** ID of the proxy to pass as x-proxy-id header */\n    proxyId: string\n    /** secret to sign x-proxy-signature header */\n    proxySecret: string\n    /** List of known proxies before current one from which IP can be extracted */\n    knownProxies?: KnownProxies\n    /**\n     * Function to determine if a given IP address should be as x-forwarded-for header source.\n     * Defaults to () => false, which means all IP addresses are trusted\n     * */\n    trustProxyFn?: TrustProxyFunction\n}\n\ntype LoggerType = {\n    error: (data: unknown) => void\n}\n\ntype RelativeOrAbsoluteEndpoint = string\n\nexport type ProxyOptions = {\n    /** Name of the proxy. Primarily used to set \"via\" header */\n    name: string\n    /** Proxy prefix which will be removed from request url  */\n    proxyPrefix: string\n    /** Upstream host to proxy requests to */\n    upstreamOrigin: string\n    /** Upstream prefix to add to request url */\n    upstreamPrefix: string\n    /** IP proxying options, if specified, IP will be passed used signed x-proxy-id, x-proxy-ip, x-proxy-timestamp, x-proxy-signature headers */\n    ipProxying?: IpProxyingOptions\n    /** \n     * Map of location header rewrites for redirects. \n     * Key: upstream location, Value: rewritten location for client.\n     * Used to rewrite Location headers in 3xx redirect responses.\n     */\n    locationRewrites?: Record<RelativeOrAbsoluteEndpoint, RelativeOrAbsoluteEndpoint>\n    /** \n     * Map of cookie path rewrites for Set-Cookie headers.\n     * Key: upstream cookie path, Value: rewritten path for client.\n     * Used to adjust cookie scope when proxying between different path prefixes.\n     */\n    cookiePathRewrites?: Record<RelativeOrAbsoluteEndpoint, RelativeOrAbsoluteEndpoint>\n    /** \n     * Logger instance for error reporting. Defaults to console if not provided.\n     * Must implement an error method that accepts any data type.\n     */\n    logger?: LoggerType\n}\n\ntype ProxyHandler = (req: IncomingMessage, res: ServerResponse) => void\n\nexport function createProxy (options: ProxyOptions): ProxyHandler {\n    const {\n        name,\n        proxyPrefix,\n        upstreamOrigin,\n        upstreamPrefix,\n        ipProxying,\n        locationRewrites,\n        cookiePathRewrites,\n        logger = console,\n    } = options\n\n    const proxy = httpProxy.createProxy({\n        target: upstreamOrigin,\n        changeOrigin: true,\n    })\n\n    const trustProxyFn = ipProxying?.trustProxyFn ?? (() => false)\n\n    proxy.on('proxyReq', (proxyReq, req) => {\n        if (req.url?.startsWith(proxyPrefix)) {\n            proxyReq.path = upstreamPrefix + req.url.slice(proxyPrefix.length)\n        }\n        proxyReq.setHeader('via', name)\n\n        if (req.url && req.method && ipProxying) {\n            const ip = getRequestIp(req, trustProxyFn, ipProxying.knownProxies)\n            const headers = getProxyHeadersForIp(req.method, proxyReq.path, ip, ipProxying.proxyId, ipProxying.proxySecret)\n            for (const [headerName, headerValue] of Object.entries(headers)) {\n                proxyReq.setHeader(headerName, headerValue)\n            }\n        }\n    })\n    proxy.on('proxyRes', (proxyRes, _req, _res) => {\n        if (proxyRes.headers.location) {\n            proxyRes.headers.location = replaceUpstreamEndpoint({\n                endpoint: proxyRes.headers.location,\n                proxyPrefix,\n                upstreamPrefix,\n                upstreamOrigin,\n                rewrites: locationRewrites,\n            })\n        }\n\n        // Handle Set-Cookie headers to rewrite cookie paths\n        const setCookieHeaders = proxyRes.headers['set-cookie']\n        if (setCookieHeaders) {\n            proxyRes.headers['set-cookie'] = setCookieHeaders.map(cookieString => {\n                return cookieString.replace(/;\\s*Path=([^;]+)/i, (match, pathValue) => {\n                    const rewrittenPath = replaceUpstreamEndpoint({\n                        endpoint: pathValue,\n                        proxyPrefix,\n                        upstreamPrefix,\n                        upstreamOrigin,\n                        rewrites: cookiePathRewrites,\n                    })\n                    return match.replace(pathValue, rewrittenPath)\n                })\n            })\n        }\n    })\n\n    return function syncProxyHandler (req, res) {\n        proxy.web(req, res, {}, (err) => {\n            if (err) {\n                // TODO: Add more complex loggers and standard error handling in next iterations\n                logger.error({ msg: 'Proxy error', err })\n                if (!res.headersSent) {\n                    res.writeHead(502, { 'Content-Type': 'application/json' })\n                    res.end(JSON.stringify({ errors: [{ message: 'Proxy error' }] }))\n                } else {\n                    res.end()\n                }\n            }\n        })\n    }\n}","import { getCookie, setCookie } from 'cookies-next'\n\nimport { getEmbeddingContext } from './embeddingContext'\nimport { generateUUIDv4 } from './uuid'\n\ntype SenderInfo = {\n    dv: number\n    fingerprint: string\n}\n\n/** Name of the cookie in which the fingerprint will be stored */\nexport const FINGERPRINT_ID_COOKIE_NAME = 'fingerprint'\n/** Default fingerprint length */\nexport const FINGERPRINT_ID_LENGTH = 32\n/** Fingerprint cookie should be persistent, so we are giving it a big maxAge */\nconst VERY_LONG_MAX_AGE_IN_SECONDS = Math.pow(2, 31) - 1 // Around 68 years in seconds\n\nfunction makeId (length: number): string {\n    const croppedLength = Math.min(length, 32)\n\n    return generateUUIDv4().replaceAll('-', '').substring(0, croppedLength)\n}\n\nexport function generateFingerprint (): string {\n    return makeId(FINGERPRINT_ID_LENGTH)\n}\n\n/**\n * Creates a device fingerprint in the browser environment\n * that can be used to send mutations in open-condo applications,\n * uses cookies for storage between sessions.\n * Mostly used to generate the sender field in getClientSideSenderInfo.\n * So consider using it instead\n */\nexport function getClientSideFingerprint (): string {\n    // NOTE: if we're inside another application, use its device id as fingerprint, but not persist it in cookie\n    const embeddingContext = getEmbeddingContext()\n    if (embeddingContext) {\n        return embeddingContext.ctx.device.id\n    }\n\n    let fingerprint = getCookie(FINGERPRINT_ID_COOKIE_NAME)\n    if (!fingerprint) {\n        fingerprint = generateFingerprint()\n    }\n    // Since 2022 browsers maintain cookie for 400 days at max, so let's update cookie expiration time\n    setCookie(FINGERPRINT_ID_COOKIE_NAME, fingerprint, {\n        maxAge: VERY_LONG_MAX_AGE_IN_SECONDS, // no \"maxAge\" or \"expires\" means that cookie clears when session ends (f.e. when browser closes)\n    })\n\n    return fingerprint\n}\n\n/**\n * Creates a device fingerprint in the browser environment\n * that can be used to send mutations in open-condo applications.\n * Uses cookies for storage between sessions\n * @example\n *  submitReadingsMutation({\n *     variables: {\n *         data: {\n *             ...values,\n *             dv: 1,\n *             sender: getClientSideSenderInfo(),\n *             meter: { connect: { id: meter.id } },\n *             source: { connect: { id: METER_READING_MOBILE_APP_SOURCE_ID } },\n *         },\n *     },\n * })\n */\nexport function getClientSideSenderInfo (): SenderInfo {\n    return {\n        dv: 1,\n        fingerprint: getClientSideFingerprint(),\n    }\n}\n","import { deleteCookie, getCookie, setCookie } from 'cookies-next'\nimport React, { useEffect, useMemo, useState, createContext, useContext } from 'react'\nimport { z } from 'zod'\n\nimport { generateUUIDv4 } from './uuid'\n\nimport type { AppType, Optional } from './common/types'\nimport type { IncomingMessage, ServerResponse } from 'http'\n\nconst EMBEDDING_CONTEXT_COOKIE_NAME = 'embeddingContext'\nconst EMBEDDING_CONTEXT_QUERY_PARAM = 'embeddingContext'\nconst EMBEDDING_CONTEXT_PRIMARY_TAB_SESSION_STORAGE_KEY = 'isEmbeddingContextProvider'\nconst EMBEDDING_CONTEXT_PROP_NAME = '__EMBEDDING_CONTEXT__'\nconst EMBEDDING_CONTEXT_CLEANUP_POLLING_TIMEOUT_IN_MS = 2_000\n\nconst EMBEDDING_CONTEXT_SCHEMA = z.strictObject({\n    dv: z.literal(1),\n    app: z.strictObject({\n        id: z.string(),\n        version: z.string().optional(),\n        build: z.string().optional(),\n    }),\n    platform: z.enum(['iOS', 'Android', 'web']),\n    os: z.strictObject({\n        name: z.string(),\n        version: z.string().optional(),\n    }).optional(),\n    device: z.strictObject({\n        id: z.string(),\n    }),\n})\n\nconst EMBEDDING_CONTEXT_WITH_SOURCE_SCHEMA = z.strictObject({\n    ctx: EMBEDDING_CONTEXT_SCHEMA,\n    source: z.enum(['query', 'cookie']),\n})\n\nconst IS_PRIMARY_ALIVE_MESSAGE_SCHEMA = z.object({\n    type: z.literal('EmbeddingContextPrimaryPolling'),\n    data: z.strictObject({\n        requestId: z.string(),\n    }),\n})\n\nconst IS_PRIMARY_ALIVE_RESPONSE_SCHEMA = z.object({\n    type: z.literal('EmbeddingContextPrimaryPollingResult'),\n    data: z.strictObject({\n        requestId: z.string(),\n        isPrimary: z.boolean(),\n    }),\n})\n\nexport type EmbeddingContext = z.infer<typeof EMBEDDING_CONTEXT_SCHEMA>\ntype EmbeddingContextWithSource = z.infer<typeof EMBEDDING_CONTEXT_WITH_SOURCE_SCHEMA>\ntype IsPrimaryAliveMessage = z.infer<typeof IS_PRIMARY_ALIVE_MESSAGE_SCHEMA>\ntype IsPrimaryAliveResponse = z.infer<typeof IS_PRIMARY_ALIVE_RESPONSE_SCHEMA>\n\nconst ReactEmbeddingContext = createContext<EmbeddingContext | null>(null)\n\nexport function useEmbeddingContext (): EmbeddingContext | null {\n    return useContext(ReactEmbeddingContext)\n}\n\nfunction b64toContext (b64: string): EmbeddingContext | null {\n    try {\n        const bytes = Uint8Array.from(atob(b64), c => c.charCodeAt(0))\n        const decodedUTFString = new TextDecoder().decode(bytes)\n        const parsedCtx = JSON.parse(decodedUTFString)\n        return EMBEDDING_CONTEXT_SCHEMA.parse(parsedCtx)\n    } catch {\n        return null\n    }\n}\n\nfunction contextToB64 (ctx: EmbeddingContext): string {\n    const stringCtx = JSON.stringify(ctx)\n    const bytes = new TextEncoder().encode(stringCtx)\n    return btoa(String.fromCharCode(...bytes))\n}\n\nexport function getEmbeddingContext (req?: Optional<IncomingMessage>, res?: Optional<ServerResponse>): EmbeddingContextWithSource | null {\n    // NOTE: context can be found in query for primary tab\n    try {\n        const queryParamValue = req\n            ? new URL(req.url ?? '/', 'https://_').searchParams.get(EMBEDDING_CONTEXT_QUERY_PARAM)\n            : new URLSearchParams(window.location.search).get(EMBEDDING_CONTEXT_QUERY_PARAM)\n        if (queryParamValue) {\n            const ctx = b64toContext(decodeURIComponent(queryParamValue))\n            if (ctx) return { ctx, source: 'query' }\n        }\n    } catch {\n        // NOTE: decodeURIComponent might throw on invalid input, ignore it as non-valid query-param\n    }\n\n    // NOTE: context can be found in cookie for secondary tabs\n    const cookieValue = getCookie(EMBEDDING_CONTEXT_COOKIE_NAME, { req, res })\n    if (cookieValue) {\n        const ctx = b64toContext(cookieValue)\n        if (ctx) return { ctx, source: 'cookie' }\n    }\n\n    return null\n}\n\nexport function withEmbeddingContext<\n    PropsType extends Record<string, unknown>,\n    ComponentType,\n    RouterType,\n> (App: AppType<PropsType, ComponentType, RouterType>): AppType<PropsType, ComponentType, RouterType> {\n    const WithEmbeddingContext: AppType<PropsType, ComponentType, RouterType> = (props) => {\n        const { pageProps } = props\n\n        const propsContextWithSource = useMemo(() => {\n            const { success, data } = EMBEDDING_CONTEXT_WITH_SOURCE_SCHEMA.safeParse(pageProps[EMBEDDING_CONTEXT_PROP_NAME])\n            if (!success) return null\n            return data\n        }, [pageProps])\n\n        const [embeddingContext, setEmbeddingContext] = useState<EmbeddingContext | null>(propsContextWithSource?.ctx ?? null)\n        const [isPrimaryTab, setIsPrimaryTab] = useState<boolean | null>(propsContextWithSource?.source === 'query' ? true : null)\n        const [bcChannel, setBCChannel] = useState<BroadcastChannel | null>(null)\n\n        useEffect(() => {\n            // NOTE: if primary tab, save it in session storage, so it won't be lost on user navigation\n            if (isPrimaryTab === true && typeof window !== 'undefined') {\n                window.sessionStorage.setItem(EMBEDDING_CONTEXT_PRIMARY_TAB_SESSION_STORAGE_KEY, 'true')\n            }\n            // NOTE: restore primary tab status if it was lost on navigation\n            if (isPrimaryTab === null && typeof window !== 'undefined') {\n                setIsPrimaryTab(window.sessionStorage.getItem(EMBEDDING_CONTEXT_PRIMARY_TAB_SESSION_STORAGE_KEY) === 'true')\n            }\n        }, [isPrimaryTab])\n\n        useEffect(() => {\n            if (typeof window === 'undefined' || !('BroadcastChannel' in window)) return\n\n            const bc = new BroadcastChannel('embeddingContext')\n            setBCChannel(bc)\n\n            return () => {\n                bc.close()\n                setBCChannel(null)\n            }\n        }, [])\n\n        // NOTE: Embedding context is shared between tabs by browser technology (cookies)\n        // so each new page can obtain it in initial props in SSR and CSR\n        // The problem is cookie might not be cleaned up on tab close, so we use 2 tricks:\n        // 1. save primary tab status in session storage, so it won't be lost on user navigation\n        // 2. use BroadcastChannel to poll if primary tab is still alive, if not, clean up cookie\n        useEffect(() => {\n            if (isPrimaryTab === null || !bcChannel) return\n\n            if (isPrimaryTab) {\n                const primaryListener = (e: MessageEvent) => {\n                    const { success, data } = IS_PRIMARY_ALIVE_MESSAGE_SCHEMA.safeParse(e.data)\n                    if (!success || !data?.data?.requestId) return\n\n                    const response: IsPrimaryAliveResponse = {\n                        type: 'EmbeddingContextPrimaryPollingResult',\n                        data: {\n                            isPrimary: true,\n                            requestId: data.data.requestId,\n                        },\n                    }\n\n                    bcChannel.postMessage(response)\n                }\n\n                bcChannel.addEventListener('message', primaryListener)\n\n                return () => {\n                    bcChannel.removeEventListener('message', primaryListener)\n                }\n            }\n\n            const requestId = generateUUIDv4()\n            const timeout = setTimeout(() => {\n                deleteCookie(EMBEDDING_CONTEXT_COOKIE_NAME)\n                setEmbeddingContext(null)\n                setIsPrimaryTab(false)\n            }, EMBEDDING_CONTEXT_CLEANUP_POLLING_TIMEOUT_IN_MS)\n\n            const secondaryListener = (e: MessageEvent) => {\n                const { success, data } = IS_PRIMARY_ALIVE_RESPONSE_SCHEMA.safeParse(e.data)\n                if (!success || data?.data.requestId !== requestId) return\n\n                clearTimeout(timeout)\n            }\n\n            bcChannel.addEventListener('message', secondaryListener)\n\n            const pollMessage: IsPrimaryAliveMessage = {\n                type: 'EmbeddingContextPrimaryPolling',\n                data: {\n                    requestId,\n                },\n            }\n            bcChannel.postMessage(pollMessage)\n\n            return () => {\n                bcChannel.removeEventListener('message', secondaryListener)\n                clearTimeout(timeout)\n            }\n\n        }, [bcChannel, isPrimaryTab])\n\n        return (\n            <ReactEmbeddingContext.Provider value={embeddingContext}>\n                <App {...props} />\n            </ReactEmbeddingContext.Provider>\n        )\n    }\n\n    const appGetInitialProps = App.getInitialProps\n    if (appGetInitialProps) {\n        WithEmbeddingContext.getInitialProps = async function (context) {\n            const appProps = await appGetInitialProps(context)\n            const { ctx } = context\n            const embeddingContextWithSource = getEmbeddingContext(ctx.req, ctx.res)\n            if (embeddingContextWithSource && embeddingContextWithSource.source === 'query') {\n                // Save context in cookie for new tabs\n                setCookie(EMBEDDING_CONTEXT_COOKIE_NAME, contextToB64(embeddingContextWithSource.ctx), {\n                    req: ctx.req,\n                    res: ctx.res,\n                })\n            }\n\n            return {\n                ...appProps,\n                pageProps: {\n                    ...appProps.pageProps,\n                    [EMBEDDING_CONTEXT_PROP_NAME]: embeddingContextWithSource,\n                },\n            }\n        }\n    }\n\n    return WithEmbeddingContext\n}","import { randomBytes } from 'crypto'\n\n/**\n * Generates v4 UUIDs in both browser and Node environments\n * @example\n * const uuid = generateUUIDv4()\n */\nexport function generateUUIDv4 (): string {\n    let randomValues: Uint8Array\n\n    if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n        // Browser or Node.js (if Node 19+ supports crypto.randomUUID)\n        return crypto.randomUUID()\n    } else if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) {\n        // Browser environment\n        randomValues = new Uint8Array(16)\n        window.crypto.getRandomValues(randomValues)\n    } else {\n        // Node.js environment\n        randomValues = randomBytes(16)\n    }\n\n    // Setting the version (4) and variant (RFC4122)\n    randomValues[6] = (randomValues[6] & 0x0f) | 0x40 // version 4\n    randomValues[8] = (randomValues[8] & 0x3f) | 0x80 // variant\n\n    return [...randomValues]\n        .map((value, index) => {\n            const hex = value.toString(16).padStart(2, '0')\n            if (index === 4 || index === 6 || index === 8 || index === 10) {\n                return `-${hex}`\n            }\n            return hex\n        })\n        .join('')\n}\n","import { parse as parseCookieString } from 'cookie'\n\nimport {\n    FINGERPRINT_ID_COOKIE_NAME,\n    getClientSideFingerprint,\n} from './sender'\nimport { generateUUIDv4 } from './uuid'\n\nexport type TracingOptions = {\n    serviceUrl: string\n    codeVersion: string\n    target?: string\n    previousHeaders?: Record<string, string>\n}\n\nconst SSR_DEFAULT_FINGERPRINT = 'webAppSSR'\nconst COOKIE_HEADER_NAME = 'cookie'\nconst REMOTE_APP_HEADER_NAME = 'x-remote-app'\nconst REMOTE_VERSION_HEADER_NAME = 'x-remote-version'\nconst REMOTE_CLIENT_HEADER_NAME = 'x-remote-client'\nconst REMOTE_ENV_HEADER_NAME = 'x-remote-env'\nconst TARGET_HEADER_NAME = 'x-target'\nconst START_REQUEST_ID_HEADER_NAME = 'x-start-request-id'\nconst PARENT_REQUEST_ID_HEADER_NAME = 'x-parent-request-id'\n\nfunction generateRequestId () {\n    return `BR${generateUUIDv4().replaceAll('-', '')}`\n}\n\nexport function getAppTracingHeaders (options: TracingOptions) {\n    const reqId = generateRequestId()\n\n    const headers: Record<string, string> = {\n        ...options.previousHeaders,\n        [REMOTE_APP_HEADER_NAME]: options.serviceUrl,\n        [REMOTE_VERSION_HEADER_NAME]: options.codeVersion,\n        [PARENT_REQUEST_ID_HEADER_NAME]: reqId,\n        [START_REQUEST_ID_HEADER_NAME]: reqId,\n    }\n\n    if (options.target) {\n        headers[TARGET_HEADER_NAME] = options.target\n    }\n\n    headers[REMOTE_ENV_HEADER_NAME] = typeof document === 'undefined' ? 'SSR' : 'CSR'\n\n    // NOTE: CSR\n    if (typeof document !== 'undefined' && document.cookie) {\n        headers[REMOTE_CLIENT_HEADER_NAME] = getClientSideFingerprint()\n    } else if (headers[COOKIE_HEADER_NAME]) {\n        const ssrCookies = parseCookieString(headers[COOKIE_HEADER_NAME])\n\n        headers[REMOTE_CLIENT_HEADER_NAME] = ssrCookies[FINGERPRINT_ID_COOKIE_NAME] || SSR_DEFAULT_FINGERPRINT\n    }\n\n    return headers\n}","/**\n * Checks whenever values is NonNullable.\n * From es5 docs NonNullable excludes null and undefined from T\n * @example\n * const collection: Array<number | null | undefined> = [1, null, 3, undefined, 5]\n * const filtered = collection.filter(nonNull) // Array<number>, so it's safe to process it\n */\nexport function nonNull<TVal> (val: TVal): val is NonNullable<TVal> {\n    return val !== null && val !== undefined\n}\n","import { getCookie } from 'cookies-next'\nimport { createContext, useContext } from 'react'\n\nimport type { IncomingMessage, ServerResponse } from 'http'\nimport type { Context } from 'react'\n\nconst SSR_COOKIES_DEFAULT_PROP_NAME = '__SSR_COOKIES__'\n\nexport type SSRCookiesContextValues<CookiesList extends ReadonlyArray<string>> = Record<CookiesList[number], string | null>\n\ntype Optional<T> = T | undefined\n\ntype SSRProps<PropsType extends Record<string, unknown>> = {\n    props?: PropsType\n}\n\ntype SSRPropsWithCookies<\n    PropsType extends Record<string, unknown>,\n    CookiesList extends ReadonlyArray<string>,\n    CookiesPropName extends string = typeof SSR_COOKIES_DEFAULT_PROP_NAME,\n> = {\n    props: PropsType & {\n        [K in CookiesPropName]?: SSRCookiesContextValues<CookiesList>\n    }\n}\n\nexport type UseSSRCookiesExtractor<\n    CookiesList extends ReadonlyArray<string>,\n    CookiesPropName extends string = typeof SSR_COOKIES_DEFAULT_PROP_NAME,\n> = <PropsType extends Record<string, unknown>>(pageParams: SSRPropsWithCookies<PropsType, CookiesList, CookiesPropName>['props']) => SSRCookiesContextValues<CookiesList>\n\nexport type UseSSRCookies<CookiesList extends ReadonlyArray<string>> = () => SSRCookiesContextValues<CookiesList>\n\n/**\n * Helper that allows you to pass cookies from the request directly to the SSR,\n * thus avoiding layout shifts and loading states.\n *\n * NOTE: You should not use this tool to pass secure http-only cookies to the client,\n * that's why each application must define the list of allowed cookies itself.\n *\n * @example Init helper and export utils for app\n * import { SSRCookiesHelper } from '@open-condo/miniapp-utils/helpers/cookies'\n * import type { SSRCookiesContextValues } from '@open-condo/miniapp-utils/helpers/cookies'\n *\n * import type { Context } from 'react'\n *\n * // NOTE: put here only cookies needed in SRR (hydration), does not put http-only cookies here\n * const VITAL_COOKIES = ['residentId', 'isLayoutMinified'] as const\n *\n * const cookieHelper = new SSRCookiesHelper(VITAL_COOKIES)\n *\n * export const extractSSRCookies = cookieHelper.extractSSRCookies\n * export const useSSRCookiesExtractor = cookieHelper.generateUseSSRCookiesExtractorHook()\n * export const useSSRCookies = cookieHelper.generateUseSSRCookiesHook()\n * export const SSRCookiesContext = cookieHelper.getContext() as Context<SSRCookiesContextValues<typeof VITAL_COOKIES>>\n *\n * @example Extract cookies in getServerSideProps / getInitialProps\n * import { extractSSRCookies } from '@/domains/common/utils/ssr'\n *\n * export const getServerSideProps = async ({ req, res }) => {\n *     return extractSSRCookies(req, res, {\n *         props: { ... }\n *     })\n * }\n *\n * @example Pass extracted cookies to React context in your _app.ts\n * import { SSRCookiesContext } from '@/domains/common/utils/ssr'\n *\n * export default function App ({ Component, pageProps }: AppProps): ReactNode {\n *     const ssrCookies = useSSRCookiesExtractor(pageProps)\n *\n *     return (\n *         <SSRCookiesContext.Provider value={ssrCookies}>\n *             <Component {...pageProps} />\n *         </SSRCookiesContext.Provider>\n *     )\n * }\n *\n * @example Use extracted cookies anywhere in your app.\n * // /domains/common/components/Layout.tsx\n * import { useState } from 'react'\n * import { useSSRCookies } from '@/domains/common/utils/ssr'\n *\n * import type { FC } from 'react'\n *\n * export const Layout: FC = () => {\n *     const { isLayoutMinified } = useSSRCookies()\n *\n *     const [layoutMinified, setLayoutMinified] = useState(isLayoutMinified === 'true')\n *\n *     return {\n *         // ...\n *     }\n * }\n */\nexport class SSRCookiesHelper<\n    CookiesList extends ReadonlyArray<string>,\n    CookiesPropName extends string = typeof SSR_COOKIES_DEFAULT_PROP_NAME,\n> {\n    allowedCookies: CookiesList\n    propName: CookiesPropName\n    private readonly context: Context<SSRCookiesContextValues<CookiesList>>\n    private readonly defaultValues: SSRCookiesContextValues<CookiesList>\n\n    constructor (allowedCookies: CookiesList, propName?: CookiesPropName) {\n        this.allowedCookies = allowedCookies\n        this.propName = propName || SSR_COOKIES_DEFAULT_PROP_NAME as CookiesPropName\n        this.defaultValues = Object.fromEntries(allowedCookies.map(key => [key, null])) as SSRCookiesContextValues<CookiesList>\n        this.context = createContext<SSRCookiesContextValues<CookiesList>>(this.defaultValues)\n\n        this.extractSSRCookies = this.extractSSRCookies.bind(this)\n    }\n\n    getContext (): Context<SSRCookiesContextValues<CookiesList>> {\n        return this.context\n    }\n\n    generateUseSSRCookiesExtractorHook (): UseSSRCookiesExtractor<CookiesList, CookiesPropName> {\n        const defaultValues = this.defaultValues\n        const propName = this.propName\n\n        return function useSSRCookiesExtractor<PropsType extends Record<string, unknown>> (\n            pageProps: SSRPropsWithCookies<PropsType, CookiesList, CookiesPropName>['props']\n        ): SSRCookiesContextValues<CookiesList> {\n            return pageProps[propName] || defaultValues\n        }\n    }\n\n    generateUseSSRCookiesHook (): UseSSRCookies<CookiesList> {\n        const context = this.context\n\n        return function useSSRCookies (): SSRCookiesContextValues<CookiesList> {\n            return useContext(context)\n        }\n    }\n\n    extractSSRCookies<PropsType extends Record<string, unknown>> (\n        req: Optional<IncomingMessage>,\n        res: Optional<ServerResponse>,\n        pageParams: SSRProps<PropsType>\n    ): SSRPropsWithCookies<PropsType, CookiesList, CookiesPropName>  {\n        return {\n            ...pageParams,\n            props: {\n                ...pageParams.props,\n                [this.propName]: Object.fromEntries(\n                    Object.keys(this.defaultValues).map(key => [\n                        key,\n                        getCookie(key, { req, res }) || null,\n                    ])\n                ),\n            },\n        } as SSRPropsWithCookies<PropsType, CookiesList, CookiesPropName>\n    }\n}\n","/**\n * Check whether it's a server or client environment\n * @example\n * if (!isSSR()) {\n *     console.log(window.location.href)\n * }\n */\nexport function isSSR (): boolean {\n    return typeof window === 'undefined'\n}\n\n/**\n * Check whether it's development environment or not\n * @example\n * const IS_DEBUG_LOGS_ENABLED = isDebug()\n */\nexport function isDebug (): boolean {\n    return process.env.NODE_ENV === 'development'\n}\n","type Rewrite = {\n    source: string\n    destination: string\n}\n\nconst POSTHOG_CLOUD_HOST_BASE = 'i.posthog.com'\nconst POSTHOG_CLOUD_HOST_MATCHER = new RegExp(`^(\\\\w+)\\\\.${POSTHOG_CLOUD_HOST_BASE.replaceAll('.', '\\\\.')}$`)\n\n/**\n * Gets posthog endpoint based on its domain and requested path.\n * Used to support both cloud and self-hosted instances, which differs in set of endpoints.\n * See example below for detailed explanation:\n * @example\n * getPosthogPath('https://eu.i.posthog.com', ['static', 'something']) // https://eu-assets.i.posthog.com/something\n * getPosthogPath('https://eu.i.posthog.com', ['other', 'path']) // https://eu.i.posthog.com/other/path\n * getPosthogPath('https://ph.self-hosted.com', ['static', 'something']) // https://ph.self-hosted.com/static/something\n * getPosthogPath('https://ph.self-hosted.com', ['other', 'path']) // https://ph.self-hosted.com/other/path\n */\nexport function getPosthogEndpoint (posthogDomain: string, requestedPath: Array<string>): string {\n    const posthogURL = new URL(posthogDomain)\n\n    const cloudMatch = posthogURL.host.match(POSTHOG_CLOUD_HOST_MATCHER)\n    if (cloudMatch && cloudMatch.length > 1) {\n        const region = cloudMatch[1]\n        if (requestedPath.length && requestedPath[0] === 'static') {\n            posthogURL.host = `${region}-assets.${POSTHOG_CLOUD_HOST_BASE}`\n            posthogURL.pathname = requestedPath.slice(1).join('/')\n            return posthogURL.toString()\n        }\n    }\n\n    posthogURL.pathname = requestedPath.join('/')\n    return posthogURL.toString()\n}\n\n/**\n * Generates Next.js rewrites based on PostHog domain,\n * so that PostHog can run in cloud and self-hosted versions without any ad blocker restrictions\n * @example\n * generateRewrites('https://eu.i.posthog.com', '/api/posthog')\n * generateRewrites('https://posthog.my.domain.com', '/api/posthog')\n * generateRewrites(process.env.NEXT_PUBLIC_POSTHOG_HOST, '/api/posthog')\n *\n * @deprecated This util is not used in condo applications and will be removed in next major upgrade,\n * since it requires knowing postHogDomain during build time. Consider setting up http proxy on API route instead\n */\nexport function generateRewrites (postHogDomain: string, routeEndpoint: string): Array<Rewrite> {\n    const url = new URL(postHogDomain)\n\n    const match = url.host.match(POSTHOG_CLOUD_HOST_MATCHER)\n\n    // Cloud PH must have separate url for assets\n    // SRC: https://posthog.com/docs/advanced/proxy/nextjs\n    if (match && match.length > 1) {\n        const region = match[1]\n        return [\n            {\n                source: `${routeEndpoint}/static/:path*`,\n                destination: `https://${region}-assets.${POSTHOG_CLOUD_HOST_BASE}/static/:path*`,\n            },\n            {\n                source: `${routeEndpoint}/:path*`,\n                destination: `https://${region}.${POSTHOG_CLOUD_HOST_BASE}/:path*`,\n            },\n        ]\n    }\n\n    return [\n        {\n            source: `${routeEndpoint}/:path*`,\n            destination: `${postHogDomain}/:path*`,\n        },\n    ]\n}","import { useEffect, useRef } from 'react'\n\nimport type { CondoBridge, CondoBridgeSubscriptionListener } from '@open-condo/bridge'\n\nexport type ActionHandler = (actionId: string) => void\nexport type ActionHandlers = Partial<Record<string, ActionHandler>>\n\n/**\n * Subscribes to host action events and routes them to local handlers.\n *\n * Use it with host-provided actions that return `actionsIds` after configuration.\n * Those actions are rendered by the host in a platform-specific location\n * (e.g., bottom action bar on web or top-bar icons on mobile). Pass a map of\n * `{ [actionId]: handler }` so your miniapp can react to clicks on those actions.\n */\nexport function useSetPageActionsHandlers (bridge: CondoBridge, handlers: ActionHandlers): void {\n    const handlersRef = useRef<ActionHandlers>(handlers)\n\n    useEffect(() => {\n        handlersRef.current = handlers\n    }, [handlers])\n\n    useEffect(() => {\n        const listener: CondoBridgeSubscriptionListener = (event) => {\n            if (event.type !== 'CondoWebAppActionClickEvent') return\n\n            if ('actionId' in event.data && typeof event.data.actionId === 'string') {\n                const { actionId } = event.data\n                const handler = handlersRef.current[actionId]\n                handler?.(actionId)\n            }\n        }\n\n        bridge.subscribe(listener)\n        return () => {\n            bridge.unsubscribe(listener)\n        }\n    }, [bridge])\n}\n","// SRC: https://github.com/streamich/react-use/blob/master/src/useEffectOnce.ts\n\nimport { EffectCallback, useEffect } from 'react'\n\n/**\n * useEffect wrapper, that runs side effect only once on initial component render\n * @example\n * useEffectOnce(() => {\n *     initAnalytics()\n * })\n */\nexport function useEffectOnce (cb: EffectCallback): void {\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    useEffect(cb, [])\n}\n","// Minified version of https://github.com/juliencrn/usehooks-ts/blob/master/packages/usehooks-ts/src/useIntersectionObserver/useIntersectionObserver.ts\n\nimport { useEffect, useRef, useState } from 'react'\n\ntype State = {\n    isIntersecting: boolean\n    entry?: IntersectionObserverEntry\n}\n\ntype UseIntersectionObserverOptions = {\n    threshold?: number\n    onChange?: (isIntersecting: boolean, entry: IntersectionObserverEntry) => void\n    root?: Element | Document | null\n    rootMargin?: string\n}\n\ntype IntersectionReturnType = {\n    ref: (node?: Element | null) => void\n    isIntersecting: boolean\n    entry?: IntersectionObserverEntry\n}\n\nexport function useIntersectionObserver ({\n    threshold = 0,\n    root = null,\n    rootMargin = '0%',\n    onChange,\n}: UseIntersectionObserverOptions = {}): IntersectionReturnType {\n    const [ref, setRef] = useState<Element | null | undefined>(null)\n    const [state, setState] = useState<State>(() => ({\n        isIntersecting: false,\n        entry: undefined,\n    }))\n\n    const callbackRef = useRef<UseIntersectionObserverOptions['onChange']>()\n    callbackRef.current = onChange\n\n    useEffect(() => {\n        if (!ref) return\n        if (!('IntersectionObserver' in window)) return\n\n        const observer = new IntersectionObserver((entries) => {\n            const thresholds = observer.thresholds\n\n            entries.forEach((entry) => {\n                const isIntersecting =\n                    entry.isIntersecting &&\n                    thresholds.some(threshold => entry.intersectionRatio >= threshold)\n\n                setState({ isIntersecting, entry })\n\n                if (callbackRef.current) {\n                    callbackRef.current(isIntersecting, entry)\n                }\n            })\n        }, { threshold, root, rootMargin })\n\n        observer.observe(ref)\n\n        return () => {\n            observer.disconnect()\n        }\n    }, [ref, root, rootMargin, threshold])\n\n    // ensures that if the observed element changes, the intersection observer is reinitialized\n    const prevRef = useRef<Element | null>(null)\n\n    useEffect(() => {\n        if (\n            !ref &&\n            state.entry?.target &&\n            prevRef.current !== state.entry.target\n        ) {\n            prevRef.current = state.entry.target\n            setState({ isIntersecting: false, entry: undefined })\n        }\n    }, [ref, state.entry])\n\n    return {\n        entry: state.entry,\n        ref: setRef,\n        isIntersecting: state.isIntersecting,\n    }\n}","// SRC: https://github.com/streamich/react-use/blob/master/src/usePrevious.ts\n\nimport { useEffect, useRef } from 'react'\n\n/**\n * Returns previous state value, useful for diff comparison\n * @example\n * const [count, setCount] = useState(0)\n * const prevCount = usePrevious(value)\n *\n * return (\n *      <button onClick={() => setValue(current => current + 1)}>\n *          Diff: {count - prevCount}\n *      </button>\n * )\n */\nexport function usePrevious<T> (state: T): T | undefined {\n    const ref = useRef<T>()\n\n    // Runs all the times, but after rendering, so ref.current update happens after return\n    useEffect(() => {\n        ref.current = state\n    })\n\n    return ref.current\n}\n"],"mappings":"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","names":["setCookie","check","createChecker","check","createChecker","createChecker","getCookie","setCookie","z","z","getCookie","setCookie","setCookie","getCookie","createContext","useContext","useEffect","useEffect","useEffect","useRef","useState","threshold","useEffect","useRef"]}