@tryghost/referrer-parser 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
3
  const bsky = { "medium": "social", "source": "Bluesky" };
4
+ const buffer = { "medium": "social", "source": "Buffer" };
7
5
  const knownReferrers = {
8
6
  "support.google.com": { "medium": "unknown", "source": "Google" },
9
7
  "developers.google.com": { "medium": "unknown", "source": "Google" },
@@ -2373,17 +2371,29 @@ const knownReferrers = {
2373
2371
  "mastodon.online": { "medium": "social", "source": "Mastodon" },
2374
2372
  "org.joinmastodon.android": { "medium": "social", "source": "Mastodon" },
2375
2373
  "www.instagram.com": { "medium": "social", "source": "Instagram" },
2376
- "search.brave.com": { "medium": "search", "source": "Brave Search" }
2374
+ "search.brave.com": { "medium": "search", "source": "Brave Search" },
2375
+ "x.com": { "medium": "social", "source": "Twitter" },
2376
+ "com.twitter.android": { "medium": "social", "source": "Twitter" },
2377
+ "l.threads.com": { "medium": "social", "source": "Threads" },
2378
+ "r.search.yahoo.com": { "medium": "search", "source": "Yahoo!" },
2379
+ buffer,
2380
+ "phanpy.social": { "medium": "social", "source": "Mastodon" },
2381
+ "dev.phanpy.social": { "medium": "social", "source": "Mastodon" },
2382
+ "www.memeorandum.com": { "medium": "referral", "source": "Memeorandum" },
2383
+ "memeorandum.com": { "medium": "referral", "source": "Memeorandum" },
2384
+ "ground.news": { "medium": "referral", "source": "Ground News" },
2385
+ "apple.news": { "medium": "referral", "source": "Apple News" },
2386
+ "www.smartnews.com": { "medium": "referral", "source": "SmartNews" }
2377
2387
  };
2378
2388
  class ReferrerParser {
2389
+ adminUrl;
2390
+ siteUrl;
2379
2391
  /**
2380
2392
  * Creates a new referrer parser instance
2381
2393
  *
2382
2394
  * @param options - Configuration options
2383
2395
  */
2384
2396
  constructor(options = {}) {
2385
- __publicField(this, "adminUrl");
2386
- __publicField(this, "siteUrl");
2387
2397
  this.adminUrl = this.getUrlFromStr(options.adminUrl || "");
2388
2398
  this.siteUrl = this.getUrlFromStr(options.siteUrl || "");
2389
2399
  }
@@ -2401,45 +2411,45 @@ class ReferrerParser {
2401
2411
  return {
2402
2412
  referrerSource: "Ghost Explore",
2403
2413
  referrerMedium: "Ghost Network",
2404
- referrerUrl: (referrerUrl == null ? void 0 : referrerUrl.hostname) ?? null
2414
+ referrerUrl: referrerUrl?.hostname ?? null
2405
2415
  };
2406
2416
  }
2407
2417
  if (this.isGhostOrgUrl(referrerUrl)) {
2408
2418
  return {
2409
2419
  referrerSource: "Ghost.org",
2410
2420
  referrerMedium: "Ghost Network",
2411
- referrerUrl: (referrerUrl == null ? void 0 : referrerUrl.hostname) ?? null
2421
+ referrerUrl: referrerUrl?.hostname ?? null
2412
2422
  };
2413
2423
  }
2414
2424
  if (referrerSource && this.isGhostNewsletter({ referrerSource })) {
2415
2425
  return {
2416
2426
  referrerSource: referrerSource.replace(/-/g, " "),
2417
2427
  referrerMedium: "Email",
2418
- referrerUrl: (referrerUrl == null ? void 0 : referrerUrl.hostname) ?? null
2428
+ referrerUrl: referrerUrl?.hostname ?? null
2419
2429
  };
2420
2430
  }
2421
2431
  if (referrerSource) {
2422
2432
  const urlData = this.getDataFromUrl(referrerUrl);
2423
2433
  const knownSource = Object.values(knownReferrers).find((referrer) => referrer.source.toLowerCase() === referrerSource.toLowerCase());
2424
2434
  return {
2425
- referrerSource: (knownSource == null ? void 0 : knownSource.source) || referrerSource,
2426
- referrerMedium: (knownSource == null ? void 0 : knownSource.medium) || referrerMedium || (urlData == null ? void 0 : urlData.medium) || null,
2427
- referrerUrl: (referrerUrl == null ? void 0 : referrerUrl.hostname) ?? null
2435
+ referrerSource: knownSource?.source || referrerSource,
2436
+ referrerMedium: knownSource?.medium || referrerMedium || urlData?.medium || null,
2437
+ referrerUrl: referrerUrl?.hostname ?? null
2428
2438
  };
2429
2439
  }
2430
2440
  if (!this.isSiteDomain(referrerUrl)) {
2431
2441
  const urlData = this.getDataFromUrl(referrerUrl);
2432
2442
  if (urlData) {
2433
2443
  return {
2434
- referrerSource: (urlData == null ? void 0 : urlData.source) ?? null,
2435
- referrerMedium: (urlData == null ? void 0 : urlData.medium) ?? null,
2436
- referrerUrl: (referrerUrl == null ? void 0 : referrerUrl.hostname) ?? null
2444
+ referrerSource: urlData?.source ?? null,
2445
+ referrerMedium: urlData?.medium ?? null,
2446
+ referrerUrl: referrerUrl?.hostname ?? null
2437
2447
  };
2438
2448
  }
2439
2449
  return {
2440
- referrerSource: (referrerUrl == null ? void 0 : referrerUrl.hostname) ?? null,
2450
+ referrerSource: referrerUrl?.hostname ?? null,
2441
2451
  referrerMedium: null,
2442
- referrerUrl: (referrerUrl == null ? void 0 : referrerUrl.hostname) ?? null
2452
+ referrerUrl: referrerUrl?.hostname ?? null
2443
2453
  };
2444
2454
  }
2445
2455
  return {
@@ -2496,7 +2506,17 @@ class ReferrerParser {
2496
2506
  if (!url) {
2497
2507
  return false;
2498
2508
  }
2499
- if (this.siteUrl.hostname === url.hostname) {
2509
+ const siteHostname = this.siteUrl.hostname;
2510
+ const urlHostname = url.hostname;
2511
+ if (siteHostname === urlHostname) {
2512
+ if (url.pathname.startsWith(this.siteUrl.pathname)) {
2513
+ return true;
2514
+ }
2515
+ return false;
2516
+ }
2517
+ const siteWithoutWww = siteHostname.replace(/^www\./, "");
2518
+ const urlWithoutWww = urlHostname.replace(/^www\./, "");
2519
+ if (siteWithoutWww === urlWithoutWww) {
2500
2520
  if (url.pathname.startsWith(this.siteUrl.pathname)) {
2501
2521
  return true;
2502
2522
  }
@@ -2538,14 +2558,13 @@ class ReferrerParser {
2538
2558
  * @returns True if the referrer is from Ghost Explore
2539
2559
  */
2540
2560
  isGhostExploreRef({ referrerUrl, referrerSource }) {
2541
- var _a, _b, _c;
2542
2561
  if (referrerSource === "ghost-explore") {
2543
2562
  return true;
2544
2563
  }
2545
2564
  if (!referrerUrl) {
2546
2565
  return false;
2547
2566
  }
2548
- if ((referrerUrl == null ? void 0 : referrerUrl.hostname) && ((_a = this.adminUrl) == null ? void 0 : _a.hostname) === (referrerUrl == null ? void 0 : referrerUrl.hostname) && ((_c = referrerUrl == null ? void 0 : referrerUrl.pathname) == null ? void 0 : _c.startsWith((_b = this.adminUrl) == null ? void 0 : _b.pathname))) {
2567
+ if (referrerUrl?.hostname && this.adminUrl?.hostname === referrerUrl?.hostname && referrerUrl?.pathname?.startsWith(this.adminUrl?.pathname)) {
2549
2568
  return true;
2550
2569
  }
2551
2570
  if (referrerUrl.hostname === "ghost.org" && referrerUrl.pathname.startsWith("/explore")) {
@@ -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 if (this.siteUrl.hostname === url.hostname) {\n if (url.pathname.startsWith(this.siteUrl.pathname)) {\n return true;\n }\n return false;\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;AAAA;AA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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 CHANGED
@@ -1,7 +1,5 @@
1
- var __defProp = Object.defineProperty;
2
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
1
  const bsky = { "medium": "social", "source": "Bluesky" };
2
+ const buffer = { "medium": "social", "source": "Buffer" };
5
3
  const knownReferrers = {
6
4
  "support.google.com": { "medium": "unknown", "source": "Google" },
7
5
  "developers.google.com": { "medium": "unknown", "source": "Google" },
@@ -2371,17 +2369,29 @@ const knownReferrers = {
2371
2369
  "mastodon.online": { "medium": "social", "source": "Mastodon" },
2372
2370
  "org.joinmastodon.android": { "medium": "social", "source": "Mastodon" },
2373
2371
  "www.instagram.com": { "medium": "social", "source": "Instagram" },
2374
- "search.brave.com": { "medium": "search", "source": "Brave Search" }
2372
+ "search.brave.com": { "medium": "search", "source": "Brave Search" },
2373
+ "x.com": { "medium": "social", "source": "Twitter" },
2374
+ "com.twitter.android": { "medium": "social", "source": "Twitter" },
2375
+ "l.threads.com": { "medium": "social", "source": "Threads" },
2376
+ "r.search.yahoo.com": { "medium": "search", "source": "Yahoo!" },
2377
+ buffer,
2378
+ "phanpy.social": { "medium": "social", "source": "Mastodon" },
2379
+ "dev.phanpy.social": { "medium": "social", "source": "Mastodon" },
2380
+ "www.memeorandum.com": { "medium": "referral", "source": "Memeorandum" },
2381
+ "memeorandum.com": { "medium": "referral", "source": "Memeorandum" },
2382
+ "ground.news": { "medium": "referral", "source": "Ground News" },
2383
+ "apple.news": { "medium": "referral", "source": "Apple News" },
2384
+ "www.smartnews.com": { "medium": "referral", "source": "SmartNews" }
2375
2385
  };
2376
2386
  class ReferrerParser {
2387
+ adminUrl;
2388
+ siteUrl;
2377
2389
  /**
2378
2390
  * Creates a new referrer parser instance
2379
2391
  *
2380
2392
  * @param options - Configuration options
2381
2393
  */
2382
2394
  constructor(options = {}) {
2383
- __publicField(this, "adminUrl");
2384
- __publicField(this, "siteUrl");
2385
2395
  this.adminUrl = this.getUrlFromStr(options.adminUrl || "");
2386
2396
  this.siteUrl = this.getUrlFromStr(options.siteUrl || "");
2387
2397
  }
@@ -2399,45 +2409,45 @@ class ReferrerParser {
2399
2409
  return {
2400
2410
  referrerSource: "Ghost Explore",
2401
2411
  referrerMedium: "Ghost Network",
2402
- referrerUrl: (referrerUrl == null ? void 0 : referrerUrl.hostname) ?? null
2412
+ referrerUrl: referrerUrl?.hostname ?? null
2403
2413
  };
2404
2414
  }
2405
2415
  if (this.isGhostOrgUrl(referrerUrl)) {
2406
2416
  return {
2407
2417
  referrerSource: "Ghost.org",
2408
2418
  referrerMedium: "Ghost Network",
2409
- referrerUrl: (referrerUrl == null ? void 0 : referrerUrl.hostname) ?? null
2419
+ referrerUrl: referrerUrl?.hostname ?? null
2410
2420
  };
2411
2421
  }
2412
2422
  if (referrerSource && this.isGhostNewsletter({ referrerSource })) {
2413
2423
  return {
2414
2424
  referrerSource: referrerSource.replace(/-/g, " "),
2415
2425
  referrerMedium: "Email",
2416
- referrerUrl: (referrerUrl == null ? void 0 : referrerUrl.hostname) ?? null
2426
+ referrerUrl: referrerUrl?.hostname ?? null
2417
2427
  };
2418
2428
  }
2419
2429
  if (referrerSource) {
2420
2430
  const urlData = this.getDataFromUrl(referrerUrl);
2421
2431
  const knownSource = Object.values(knownReferrers).find((referrer) => referrer.source.toLowerCase() === referrerSource.toLowerCase());
2422
2432
  return {
2423
- referrerSource: (knownSource == null ? void 0 : knownSource.source) || referrerSource,
2424
- referrerMedium: (knownSource == null ? void 0 : knownSource.medium) || referrerMedium || (urlData == null ? void 0 : urlData.medium) || null,
2425
- referrerUrl: (referrerUrl == null ? void 0 : referrerUrl.hostname) ?? null
2433
+ referrerSource: knownSource?.source || referrerSource,
2434
+ referrerMedium: knownSource?.medium || referrerMedium || urlData?.medium || null,
2435
+ referrerUrl: referrerUrl?.hostname ?? null
2426
2436
  };
2427
2437
  }
2428
2438
  if (!this.isSiteDomain(referrerUrl)) {
2429
2439
  const urlData = this.getDataFromUrl(referrerUrl);
2430
2440
  if (urlData) {
2431
2441
  return {
2432
- referrerSource: (urlData == null ? void 0 : urlData.source) ?? null,
2433
- referrerMedium: (urlData == null ? void 0 : urlData.medium) ?? null,
2434
- referrerUrl: (referrerUrl == null ? void 0 : referrerUrl.hostname) ?? null
2442
+ referrerSource: urlData?.source ?? null,
2443
+ referrerMedium: urlData?.medium ?? null,
2444
+ referrerUrl: referrerUrl?.hostname ?? null
2435
2445
  };
2436
2446
  }
2437
2447
  return {
2438
- referrerSource: (referrerUrl == null ? void 0 : referrerUrl.hostname) ?? null,
2448
+ referrerSource: referrerUrl?.hostname ?? null,
2439
2449
  referrerMedium: null,
2440
- referrerUrl: (referrerUrl == null ? void 0 : referrerUrl.hostname) ?? null
2450
+ referrerUrl: referrerUrl?.hostname ?? null
2441
2451
  };
2442
2452
  }
2443
2453
  return {
@@ -2494,7 +2504,17 @@ class ReferrerParser {
2494
2504
  if (!url) {
2495
2505
  return false;
2496
2506
  }
2497
- if (this.siteUrl.hostname === url.hostname) {
2507
+ const siteHostname = this.siteUrl.hostname;
2508
+ const urlHostname = url.hostname;
2509
+ if (siteHostname === urlHostname) {
2510
+ if (url.pathname.startsWith(this.siteUrl.pathname)) {
2511
+ return true;
2512
+ }
2513
+ return false;
2514
+ }
2515
+ const siteWithoutWww = siteHostname.replace(/^www\./, "");
2516
+ const urlWithoutWww = urlHostname.replace(/^www\./, "");
2517
+ if (siteWithoutWww === urlWithoutWww) {
2498
2518
  if (url.pathname.startsWith(this.siteUrl.pathname)) {
2499
2519
  return true;
2500
2520
  }
@@ -2536,14 +2556,13 @@ class ReferrerParser {
2536
2556
  * @returns True if the referrer is from Ghost Explore
2537
2557
  */
2538
2558
  isGhostExploreRef({ referrerUrl, referrerSource }) {
2539
- var _a, _b, _c;
2540
2559
  if (referrerSource === "ghost-explore") {
2541
2560
  return true;
2542
2561
  }
2543
2562
  if (!referrerUrl) {
2544
2563
  return false;
2545
2564
  }
2546
- if ((referrerUrl == null ? void 0 : referrerUrl.hostname) && ((_a = this.adminUrl) == null ? void 0 : _a.hostname) === (referrerUrl == null ? void 0 : referrerUrl.hostname) && ((_c = referrerUrl == null ? void 0 : referrerUrl.pathname) == null ? void 0 : _c.startsWith((_b = this.adminUrl) == null ? void 0 : _b.pathname))) {
2565
+ if (referrerUrl?.hostname && this.adminUrl?.hostname === referrerUrl?.hostname && referrerUrl?.pathname?.startsWith(this.adminUrl?.pathname)) {
2547
2566
  return true;
2548
2567
  }
2549
2568
  if (referrerUrl.hostname === "ghost.org" && referrerUrl.pathname.startsWith("/explore")) {
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 if (this.siteUrl.hostname === url.hostname) {\n if (url.pathname.startsWith(this.siteUrl.pathname)) {\n return true;\n }\n return false;\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;AAAA;AAAA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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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"}
@@ -9474,5 +9474,53 @@
9474
9474
  "search.brave.com": {
9475
9475
  "medium": "search",
9476
9476
  "source": "Brave Search"
9477
+ },
9478
+ "x.com": {
9479
+ "medium": "social",
9480
+ "source": "Twitter"
9481
+ },
9482
+ "com.twitter.android": {
9483
+ "medium": "social",
9484
+ "source": "Twitter"
9485
+ },
9486
+ "l.threads.com": {
9487
+ "medium": "social",
9488
+ "source": "Threads"
9489
+ },
9490
+ "r.search.yahoo.com": {
9491
+ "medium": "search",
9492
+ "source": "Yahoo!"
9493
+ },
9494
+ "buffer": {
9495
+ "medium": "social",
9496
+ "source": "Buffer"
9497
+ },
9498
+ "phanpy.social": {
9499
+ "medium": "social",
9500
+ "source": "Mastodon"
9501
+ },
9502
+ "dev.phanpy.social": {
9503
+ "medium": "social",
9504
+ "source": "Mastodon"
9505
+ },
9506
+ "www.memeorandum.com": {
9507
+ "medium": "referral",
9508
+ "source": "Memeorandum"
9509
+ },
9510
+ "memeorandum.com": {
9511
+ "medium": "referral",
9512
+ "source": "Memeorandum"
9513
+ },
9514
+ "ground.news": {
9515
+ "medium": "referral",
9516
+ "source": "Ground News"
9517
+ },
9518
+ "apple.news": {
9519
+ "medium": "referral",
9520
+ "source": "Apple News"
9521
+ },
9522
+ "www.smartnews.com": {
9523
+ "medium": "referral",
9524
+ "source": "SmartNews"
9477
9525
  }
9478
9526
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryghost/referrer-parser",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Simple library for parsing referrer URLs",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -56,9 +56,9 @@
56
56
  "@vitest/coverage-v8": "^3.0.0",
57
57
  "eslint": "^8.52.0",
58
58
  "should": "13.2.3",
59
- "sinon": "20.0.0",
59
+ "sinon": "21.0.0",
60
60
  "typescript": "5.4.3",
61
- "vite": "^6.0.0",
61
+ "vite": "^7.0.0",
62
62
  "vite-plugin-dts": "^4.0.0",
63
63
  "vitest": "^3.0.0"
64
64
  },
@@ -68,5 +68,5 @@
68
68
  "publishConfig": {
69
69
  "access": "public"
70
70
  },
71
- "gitHead": "d45b0cd051767547378a842025b0a44da801f222"
71
+ "gitHead": "b10773947244536b8829fec0540819990c901987"
72
72
  }