{"version":3,"file":"index.cjs","sources":["../lib/ReferrerParser.ts","../index.ts"],"sourcesContent":["/**\n * Interface for parsed referrer data\n */\nexport interface ReferrerData {\n    /** The identified source of the referral traffic */\n    referrerSource: string | null;\n    /** The identified medium of the referral traffic */\n    referrerMedium: string | null;\n    /** The hostname of the referral URL */\n    referrerUrl: string | null;\n}\n\n/**\n * Configuration options for the parser\n */\nexport interface ParserOptions {\n    /** URL of the site for identifying internal traffic */\n    siteUrl?: string;\n    /** URL of the admin panel for identifying admin traffic */\n    adminUrl?: string;\n}\n\n/**\n * Interface for referrer source data\n */\ninterface ReferrerSourceData {\n    source: string;\n    medium: string;\n}\n\n// Import known referrers data\nimport knownReferrers from './referrers.json';\n\n/**\n * Parses referrer URLs to determine source and medium\n */\nexport class ReferrerParser {\n    private adminUrl: URL | null;\n    private siteUrl: URL | null;\n\n    /**\n     * Creates a new referrer parser instance\n     * \n     * @param options - Configuration options\n     */\n    constructor(options: ParserOptions = {}) {\n        this.adminUrl = this.getUrlFromStr(options.adminUrl || '');\n        this.siteUrl = this.getUrlFromStr(options.siteUrl || '');\n    }\n\n    /**\n     * Parse a referrer URL to get source, medium and hostname\n     * \n     * @param referrerUrlStr - URL of the referrer\n     * @param referrerSource - Source of the referrer\n     * @param referrerMedium - Medium of the referrer\n     * @returns Parsed referrer data with source, medium and URL. Internal referrers return null values.\n     */\n    parse(referrerUrlStr: string, referrerSource?: string, referrerMedium?: string): ReferrerData {\n        const referrerUrl = this.getUrlFromStr(referrerUrlStr);\n\n        // Luinq-specific cases\n        if (this.isLuinqExploreRef({referrerUrl, referrerSource})) {\n            return {\n                referrerSource: 'Luinq Explore',\n                referrerMedium: 'Luinq Network',\n                referrerUrl: referrerUrl?.hostname ?? null\n            };\n        }\n\n        // If referrer is luinq.com\n        if (this.isLuinqOrgUrl(referrerUrl)) {\n            return {\n                referrerSource: 'luinq.com',\n                referrerMedium: 'Luinq Network',\n                referrerUrl: referrerUrl?.hostname ?? null\n            };\n        }\n\n        // Check for Luinq Newsletter\n        if (referrerSource && this.isLuinqNewsletter({referrerSource})) {\n            return {\n                referrerSource: referrerSource.replace(/-/g, ' '),\n                referrerMedium: 'Email',\n                referrerUrl: referrerUrl?.hostname ?? null\n            };\n        }\n\n        // If referrer source is available from parameters\n        if (referrerSource) {\n            const urlData = this.getDataFromUrl(referrerUrl);\n            const knownSource = Object.values(knownReferrers as Record<string, ReferrerSourceData>).find(referrer => \n                referrer.source.toLowerCase() === referrerSource.toLowerCase());\n            \n            return {\n                referrerSource: knownSource?.source || referrerSource,\n                referrerMedium: knownSource?.medium || referrerMedium || urlData?.medium || null,\n                referrerUrl: referrerUrl?.hostname ?? null\n            };\n        }\n\n        // If referrer is known external URL\n        if (!this.isSiteDomain(referrerUrl)) {\n            const urlData = this.getDataFromUrl(referrerUrl);\n\n            // Use known source/medium if available\n            if (urlData) {\n                return {\n                    referrerSource: urlData?.source ?? null,\n                    referrerMedium: urlData?.medium ?? null,\n                    referrerUrl: referrerUrl?.hostname ?? null\n                };\n            }\n            \n            // Use the hostname as a source\n            return {\n                referrerSource: referrerUrl?.hostname ?? null,\n                referrerMedium: null,\n                referrerUrl: referrerUrl?.hostname ?? null\n            };\n        }\n\n        return {\n            referrerSource: null,\n            referrerMedium: null,\n            referrerUrl: null\n        }\n    }\n\n    /**\n     * Fetches referrer data from known external URLs\n     * \n     * @param url - The URL to match against known referrers\n     * @returns Matched referrer data or null if not found\n     */\n    getDataFromUrl(url: URL | null): ReferrerSourceData | null {\n        // Handle null url case\n        if (!url) {\n            return null;\n        }\n\n        // Allow matching both \"google.ac/products\" and \"google.ac\" as a source\n        const urlHostPath = url.hostname + url.pathname;\n        const urlDataKey = Object.keys(knownReferrers as Record<string, ReferrerSourceData>).sort((a, b) => {\n            // The longer key has higher priority so google.ac/products is selected before google.ac\n            return b.length - a.length;\n        }).find((source) => {\n            return urlHostPath.startsWith(source);\n        });\n\n        return urlDataKey ? (knownReferrers as Record<string, ReferrerSourceData>)[urlDataKey] : null;\n    }\n\n    /**\n     * Return URL object for provided URL string\n     * \n     * @param url - URL string to parse\n     * @returns Parsed URL object or null if invalid\n     */\n    getUrlFromStr(url: string): URL | null {\n        if (!url) {\n            return null;\n        }\n        \n        try {\n            return new URL(url);\n        } catch (e) {\n            return null;\n        }\n    }\n\n    /**\n     * Determine whether the provided URL is a link to the site\n     * \n     * @param url - URL to check\n     * @returns True if the URL belongs to the configured site\n     */\n    isSiteDomain(url: URL | null): boolean {\n        try {\n            // If we don't have siteUrl configured, we can't detect internal traffic\n            if (!this.siteUrl) {\n                return false;\n            }\n\n            if (!url) {\n                return false;\n            }\n\n            // Handle subdomain variations (www.example.com vs example.com)\n            const siteHostname = this.siteUrl.hostname;\n            const urlHostname = url.hostname;\n            \n            // Check for exact match first\n            if (siteHostname === urlHostname) {\n                if (url.pathname.startsWith(this.siteUrl.pathname)) {\n                    return true;\n                }\n                return false;\n            }\n            \n            // Check for www subdomain variations\n            const siteWithoutWww = siteHostname.replace(/^www\\./, '');\n            const urlWithoutWww = urlHostname.replace(/^www\\./, '');\n            \n            if (siteWithoutWww === urlWithoutWww) {\n                if (url.pathname.startsWith(this.siteUrl.pathname)) {\n                    return true;\n                }\n                return false;\n            }\n            \n            return false;\n        } catch (e) {\n            return false;\n        }\n    }\n\n    /**\n     * Determine whether referrer is a Luinq newsletter\n     * \n     * @param deps - Input parameters\n     * @returns True if the referrer is a Luinq newsletter\n     */\n    isLuinqNewsletter({referrerSource}: {referrerSource: string | null}): boolean {\n        if (!referrerSource) {\n            return false;\n        }\n        // if referrer source ends with -newsletter\n        return referrerSource.endsWith('-newsletter');\n    }\n\n    /**\n     * Determine whether referrer is a luinq.com URL\n     * \n     * @param referrerUrl - The referrer URL to check\n     * @returns True if the referrer is from luinq.com\n     */\n    isLuinqOrgUrl(referrerUrl: URL | null): boolean {\n        if (!referrerUrl) {\n            return false;\n        }\n        return referrerUrl.hostname === 'luinq.com';\n    }\n\n    /**\n     * Determine whether referrer is Luinq Explore\n     * \n     * @param deps - Input parameters\n     * @returns True if the referrer is from Luinq Explore\n     */\n    isLuinqExploreRef({referrerUrl, referrerSource}: {referrerUrl: URL | null, referrerSource?: string | null}): boolean {\n        if (referrerSource === 'luinq-explore') {\n            return true;\n        }\n\n        if (!referrerUrl) {\n            return false;\n        }\n\n        if (referrerUrl?.hostname\n            && this.adminUrl?.hostname === referrerUrl?.hostname\n            && referrerUrl?.pathname?.startsWith(this.adminUrl?.pathname)\n        ) {\n            return true;\n        }\n\n        if (referrerUrl.hostname === 'luinq.com' && referrerUrl.pathname.startsWith('/explore')) {\n            return true;\n        }\n\n        return false;\n    }\n} ","import { ReferrerParser } from './lib/ReferrerParser';\nimport type { ReferrerData, ParserOptions } from './lib/ReferrerParser';\n\n/**\n * Parse a referrer URL to get source, medium and hostname\n * \n * @param referrerUrl - URL of the referrer to parse\n * @param options - Configuration options\n * @param referrerSource - Optional source to override URL parameters\n * @param referrerMedium - Optional medium to override URL parameters\n * @returns Parsed referrer data with source, medium and URL\n * \n * @example\n * // Basic usage\n * const result = parse('https://www.google.com/search?q=luinq+cms');\n * // result: { referrerSource: 'Google', referrerMedium: 'search', referrerUrl: 'www.google.com' }\n * \n * @example\n * // With site configuration\n * const result = parse('https://example.com/blog?utm_source=newsletter', {\n *   siteUrl: 'https://example.com'\n * });\n * \n * @example\n * // With explicit source and medium\n * const result = parse('https://example.com', {}, 'newsletter', 'email');\n */\nfunction parse(\n    referrerUrl: string, \n    options: ParserOptions = {}, \n    referrerSource?: string, \n    referrerMedium?: string\n): ReferrerData {\n    const parser = new ReferrerParser(options);\n    return parser.parse(referrerUrl, referrerSource, referrerMedium);\n}\n\nexport {\n    parse,\n    ReferrerParser\n};\n\nexport type {\n    ReferrerData,\n    ParserOptions\n}; "],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCO,MAAM,eA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"}