@tryghost/referrer-parser 0.1.8 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"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 // Ghost-specific cases\n if (this.isGhostExploreRef({referrerUrl, referrerSource})) {\n return {\n referrerSource: 'Ghost Explore',\n referrerMedium: 'Ghost Network',\n referrerUrl: referrerUrl?.hostname ?? null\n };\n }\n\n // If referrer is Ghost.org\n if (this.isGhostOrgUrl(referrerUrl)) {\n return {\n referrerSource: 'Ghost.org',\n referrerMedium: 'Ghost Network',\n referrerUrl: referrerUrl?.hostname ?? null\n };\n }\n\n // Check for Ghost Newsletter\n if (referrerSource && this.isGhostNewsletter({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 Ghost newsletter\n * \n * @param deps - Input parameters\n * @returns True if the referrer is a Ghost newsletter\n */\n isGhostNewsletter({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 Ghost.org URL\n * \n * @param referrerUrl - The referrer URL to check\n * @returns True if the referrer is from Ghost.org\n */\n isGhostOrgUrl(referrerUrl: URL | null): boolean {\n if (!referrerUrl) {\n return false;\n }\n return referrerUrl.hostname === 'ghost.org';\n }\n\n /**\n * Determine whether referrer is Ghost Explore\n * \n * @param deps - Input parameters\n * @returns True if the referrer is from Ghost Explore\n */\n isGhostExploreRef({referrerUrl, referrerSource}: {referrerUrl: URL | null, referrerSource?: string | null}): boolean {\n if (referrerSource === 'ghost-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 === 'ghost.org' && 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=ghost+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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"}
1
+ {"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 // Ghost-specific cases\n if (this.isGhostExploreRef({referrerUrl, referrerSource})) {\n return {\n referrerSource: 'Ghost Explore',\n referrerMedium: 'Ghost Network',\n referrerUrl: referrerUrl?.hostname ?? null\n };\n }\n\n // If referrer is Ghost.org\n if (this.isGhostOrgUrl(referrerUrl)) {\n return {\n referrerSource: 'Ghost.org',\n referrerMedium: 'Ghost Network',\n referrerUrl: referrerUrl?.hostname ?? null\n };\n }\n\n // Check for Ghost Newsletter\n if (referrerSource && this.isGhostNewsletter({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 Ghost newsletter\n * \n * @param deps - Input parameters\n * @returns True if the referrer is a Ghost newsletter\n */\n isGhostNewsletter({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 Ghost.org URL\n * \n * @param referrerUrl - The referrer URL to check\n * @returns True if the referrer is from Ghost.org\n */\n isGhostOrgUrl(referrerUrl: URL | null): boolean {\n if (!referrerUrl) {\n return false;\n }\n return referrerUrl.hostname === 'ghost.org';\n }\n\n /**\n * Determine whether referrer is Ghost Explore\n * \n * @param deps - Input parameters\n * @returns True if the referrer is from Ghost Explore\n */\n isGhostExploreRef({referrerUrl, referrerSource}: {referrerUrl: URL | null, referrerSource?: string | null}): boolean {\n if (referrerSource === 'ghost-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 === 'ghost.org' && 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=ghost+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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"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","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 // Ghost-specific cases\n if (this.isGhostExploreRef({referrerUrl, referrerSource})) {\n return {\n referrerSource: 'Ghost Explore',\n referrerMedium: 'Ghost Network',\n referrerUrl: referrerUrl?.hostname ?? null\n };\n }\n\n // If referrer is Ghost.org\n if (this.isGhostOrgUrl(referrerUrl)) {\n return {\n referrerSource: 'Ghost.org',\n referrerMedium: 'Ghost Network',\n referrerUrl: referrerUrl?.hostname ?? null\n };\n }\n\n // Check for Ghost Newsletter\n if (referrerSource && this.isGhostNewsletter({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 Ghost newsletter\n * \n * @param deps - Input parameters\n * @returns True if the referrer is a Ghost newsletter\n */\n isGhostNewsletter({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 Ghost.org URL\n * \n * @param referrerUrl - The referrer URL to check\n * @returns True if the referrer is from Ghost.org\n */\n isGhostOrgUrl(referrerUrl: URL | null): boolean {\n if (!referrerUrl) {\n return false;\n }\n return referrerUrl.hostname === 'ghost.org';\n }\n\n /**\n * Determine whether referrer is Ghost Explore\n * \n * @param deps - Input parameters\n * @returns True if the referrer is from Ghost Explore\n */\n isGhostExploreRef({referrerUrl, referrerSource}: {referrerUrl: URL | null, referrerSource?: string | null}): boolean {\n if (referrerSource === 'ghost-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 === 'ghost.org' && 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=ghost+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,eAAe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"}
1
+ {"version":3,"file":"index.js","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 // Ghost-specific cases\n if (this.isGhostExploreRef({referrerUrl, referrerSource})) {\n return {\n referrerSource: 'Ghost Explore',\n referrerMedium: 'Ghost Network',\n referrerUrl: referrerUrl?.hostname ?? null\n };\n }\n\n // If referrer is Ghost.org\n if (this.isGhostOrgUrl(referrerUrl)) {\n return {\n referrerSource: 'Ghost.org',\n referrerMedium: 'Ghost Network',\n referrerUrl: referrerUrl?.hostname ?? null\n };\n }\n\n // Check for Ghost Newsletter\n if (referrerSource && this.isGhostNewsletter({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 Ghost newsletter\n * \n * @param deps - Input parameters\n * @returns True if the referrer is a Ghost newsletter\n */\n isGhostNewsletter({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 Ghost.org URL\n * \n * @param referrerUrl - The referrer URL to check\n * @returns True if the referrer is from Ghost.org\n */\n isGhostOrgUrl(referrerUrl: URL | null): boolean {\n if (!referrerUrl) {\n return false;\n }\n return referrerUrl.hostname === 'ghost.org';\n }\n\n /**\n * Determine whether referrer is Ghost Explore\n * \n * @param deps - Input parameters\n * @returns True if the referrer is from Ghost Explore\n */\n isGhostExploreRef({referrerUrl, referrerSource}: {referrerUrl: URL | null, referrerSource?: string | null}): boolean {\n if (referrerSource === 'ghost-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 === 'ghost.org' && 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=ghost+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,eAAe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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryghost/referrer-parser",
3
- "version": "0.1.8",
3
+ "version": "0.1.11",
4
4
  "description": "Simple library for parsing referrer URLs",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -48,19 +48,19 @@
48
48
  },
49
49
  "homepage": "https://ghost.org",
50
50
  "devDependencies": {
51
- "@types/node": "^22.0.0",
51
+ "@types/node": "^24.0.0",
52
52
  "@types/should": "^13.0.0",
53
- "@types/sinon": "^17.0.3",
54
- "@typescript-eslint/eslint-plugin": "^8.0.0",
55
- "@typescript-eslint/parser": "^8.0.0",
56
- "@vitest/coverage-v8": "^3.0.0",
53
+ "@types/sinon": "^21.0.0",
54
+ "@typescript-eslint/eslint-plugin": "^8.49.0",
55
+ "@typescript-eslint/parser": "^8.49.0",
56
+ "@vitest/coverage-v8": "^4.0.14",
57
57
  "eslint": "^8.52.0",
58
58
  "should": "13.2.3",
59
59
  "sinon": "21.0.0",
60
- "typescript": "5.4.3",
61
- "vite": "^7.0.0",
62
- "vite-plugin-dts": "^4.0.0",
63
- "vitest": "^3.0.0"
60
+ "typescript": "5.9.3",
61
+ "vite": "7.2.7",
62
+ "vite-plugin-dts": "4.5.4",
63
+ "vitest": "^4.0.14"
64
64
  },
65
65
  "engines": {
66
66
  "node": ">=16.0.0"
@@ -68,5 +68,5 @@
68
68
  "publishConfig": {
69
69
  "access": "public"
70
70
  },
71
- "gitHead": "b10773947244536b8829fec0540819990c901987"
71
+ "gitHead": "a97d783ddb64ae80592ec03468b221a4ec37948a"
72
72
  }