@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 +39 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +39 -20
- package/dist/index.js.map +1 -1
- package/lib/referrers.json +48 -0
- package/package.json +4 -4
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:
|
|
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:
|
|
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:
|
|
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:
|
|
2426
|
-
referrerMedium:
|
|
2427
|
-
referrerUrl:
|
|
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:
|
|
2435
|
-
referrerMedium:
|
|
2436
|
-
referrerUrl:
|
|
2444
|
+
referrerSource: urlData?.source ?? null,
|
|
2445
|
+
referrerMedium: urlData?.medium ?? null,
|
|
2446
|
+
referrerUrl: referrerUrl?.hostname ?? null
|
|
2437
2447
|
};
|
|
2438
2448
|
}
|
|
2439
2449
|
return {
|
|
2440
|
-
referrerSource:
|
|
2450
|
+
referrerSource: referrerUrl?.hostname ?? null,
|
|
2441
2451
|
referrerMedium: null,
|
|
2442
|
-
referrerUrl:
|
|
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
|
-
|
|
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 (
|
|
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")) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -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:
|
|
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:
|
|
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:
|
|
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:
|
|
2424
|
-
referrerMedium:
|
|
2425
|
-
referrerUrl:
|
|
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:
|
|
2433
|
-
referrerMedium:
|
|
2434
|
-
referrerUrl:
|
|
2442
|
+
referrerSource: urlData?.source ?? null,
|
|
2443
|
+
referrerMedium: urlData?.medium ?? null,
|
|
2444
|
+
referrerUrl: referrerUrl?.hostname ?? null
|
|
2435
2445
|
};
|
|
2436
2446
|
}
|
|
2437
2447
|
return {
|
|
2438
|
-
referrerSource:
|
|
2448
|
+
referrerSource: referrerUrl?.hostname ?? null,
|
|
2439
2449
|
referrerMedium: null,
|
|
2440
|
-
referrerUrl:
|
|
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
|
-
|
|
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 (
|
|
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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"}
|
package/lib/referrers.json
CHANGED
|
@@ -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.
|
|
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": "
|
|
59
|
+
"sinon": "21.0.0",
|
|
60
60
|
"typescript": "5.4.3",
|
|
61
|
-
"vite": "^
|
|
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": "
|
|
71
|
+
"gitHead": "b10773947244536b8829fec0540819990c901987"
|
|
72
72
|
}
|