Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 2 additions & 9 deletions .cspell.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
{
"allowCompoundWords": true,
"dictionaries": [
"!bash","!cpp","!csharp","dictionary-src-sh","!en-gb","!go","html",
"!latex","node","!php","!python","typescript"
"!bash","!cpp","!csharp","dictionary-custom","dictionary-src-sh","!en-gb","!go","html",
"javascript","!latex","node","!php","!python","typescript"
],
"dictionaryDefinitions": [
{
Expand All @@ -17,13 +17,6 @@
"ignoreRegExpList": [
"entities","mdanchors","regexes","urlencoded","webhosts"
],
"languageSettings": [
{
"dictionaries": ["dictionary-custom"],
"languageId": "javascript,typescript,markdown",
"locale": "*"
}
],
"patterns": [
{
"name": "entities",
Expand Down
12 changes: 9 additions & 3 deletions .cspell/dictionary-custom.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
Blogsy
Bronto
Codacy
cyclonedx
delighter
Ghostery
Klaviyo
Listrak
Matomo
Octocat
Textastic
cyclonedx
delighter
Omeda
smushing
Temu
Textastic
uglifyjs
unskim
utmstrip
Vero
22 changes: 14 additions & 8 deletions README.md

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ npm version patch # or minor, or major
git push --follow-tags

# Or manually create and push a signed tag
git tag -s 4.1.0 -m "Release version 4.1.0"
git push origin 4.1.0
git tag -s 4.1.1 -m "Release version 4.1.1"
git push origin 4.1.1
```

## Release Verification
Expand All @@ -76,10 +76,10 @@ All releases should be signed with GPG/SSH signatures for verification:

```bash
# Verify the signature on a release tag
git verify-tag 4.1.0
git verify-tag 4.1.1

# Show tag details with signature
git tag -v 4.1.0
git tag -v 4.1.1
```

### Verifying Signed Commits
Expand Down
2 changes: 1 addition & 1 deletion bookmarklets.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
{
"name": "UtmStrip",
"file": "utmstrip.bookmarklet",
"version": "2.1.0"
"version": "2.2.0"
},
{
"name": "deLighter",
Expand Down
2 changes: 1 addition & 1 deletion dist/utmstrip.bookmarklet
Original file line number Diff line number Diff line change
@@ -1 +1 @@
javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=location.pathname%2Ci=location.search%3Bif(3%3Ei.length%26%26!e.includes('%2Famp'))return%3Blet%20c=e%2Ct=i%3Bconst%20a=location.hostname%2Cn=(e%2Ci)=%3Ee.replace(i%2C'%241')%3Bif(a.includes('aliexpress.')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)aff%5F(platform%7Ctrace%5Fkey)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)algo%5F%5Bep%5Dvid=%5B%5E%26%5D%2B%2Fg)%2Ct=n(t%2C%2F(%5B%3F%26%5D)(btsid%7Cws%5Fab%5Ftest)=%5B%5E%26%5D%2B%2Fg)%2Ct=n(t%2C%2F(%5B%3F%26%5D)s%5Bcp%5Dm=%5B%5E%26%5D%2B%2Fg))%2C%2F(%7C%5C.)amazon%5C.com%24%2F.test(a)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(%5Fencoding%7Cie%7ClinkCode%7ClinkId%7Cpf%7Cpsc%7Cref%5F%7Ctag)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)p%5Bdf%5D%5Frd%5F.%2A%3F=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)(content-id%7Ccrid%7Ccv%5Fct%5Fcx%7Clanguage%7Cqid%7Csprefix%7Csr%7Cth)=%5B%5E%26%5D%2B%2Fg)%2Ct=n(t%2C%2F(%5B%3F%26%5D)asc(%5Fcampaign%7C%5Frefurl%7C%5Fsource%7Csubtag)=%5B%5E%26%5D%2B%2Fg)%2Ct=n(t%2C%2F(%5B%3F%26%5D)dib(%5Ftag)%3F=%5B%5E%26%5D%2B%2Fg))%2Ct.includes('fb%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)fb%5F(action%5Fids%7Caction%5Ftypes%7Cref%7Csource)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)(fbclid%7Chrc%7Crefsrc)=%5B%5E%26%5D%2B%2Fgi))%2Ct.includes('action%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)action%5F(object%7Cref%7Ctype)%5Fmap=%5B%5E%26%5D%2B%2Fgi))%2Ct=n(t%2C%2F(%5B%3F%26%5D)(assetType%7CelqTrack%7Cmkt%5Ftok%7CoriginalReferer%7Creferrer%7Cterminal%5Fid%7Ctrk%7CtrkCampaign%7CtrkInfo)=%5B%5E%26%5D%2B%2Fgi)%2Ct.toLowerCase().includes('id=')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(an%7Casset%7Ccampaign%7Ce%7Cgcl%7Crecipient%7Csite)id=%5B%5E%26%5D%2B%2Fgi))%2C(t.includes('ga%5F')%7C%7Ct.includes('utm%5F'))%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(ga%7Cutm)%5F(campaign%7Ccid%7Ccontent%7Cdesign%7Cmedium%7Cname%7Cplace%7Cpubreferrer%7Creader%7Csource%7Cswu%7Cterm%7Cuserid%7Cviz%5Fid)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)gcl(id%7Csrc)=%5B%5E%26%5D%2B%2Fgi))%2C(%2F(m%7Cwww)%5C.youtube%5C.com%24%2F.test(a)%7C%7C'youtu.be'===a%7C%7C'www.youtube-nocookie.com'===a)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(ac%7Cannotation%5Fid%7Capp%7Cfeature%7Cgclid%7Ckw%7Csrc%5Fvid)=%5B%5E%26%5D%2B%2Fgi))%2C%2F%5B%3F%26%5D%5Fhs(enc%7Cmi)=%2F.test(t)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)%5Fhs(enc%7Cmi)=%5B%5E%26%5D%2B%2Fgi))%2Ct.includes('hmb%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)hmb%5F(campaign%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi))%2Ct.includes('cm%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)cm%5F(mmc%7Cmmca%5Cd%2B%7Cre%7Csp)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)manual%5Fcm%5Fmmc=%5B%5E%26%5D%2B%2Fgi))%2C%2F%5B%3F%26%5Dmc%5F%5Bce%5Did=%2F.test(t)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)mc%5F%5Bce%5Did=%5B%5E%26%5D%2B%2Fgi))%2C%2F%5B%3F%26%5D(iesrc%7Cmkt%5Ftok)=%2F.test(t)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(iesrc%7Cmkt%5Ftok)=%5B%5E%26%5D%2B%2Fgi))%2Ct.includes('pk%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)pk%5F(campaign%7Ccontent%7Ckwd%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi))%2Ct=t.replace(%2F%26%26%2B%2Fg%2C'%26').replace(%2F%26%24%2F%2C'')%2Ct='%3F'===t%5B0%5D%3Ft.replace(%2F%5E%5C%3F%26%2F%2C'%3F'):'%3F'%2Bt%2Ct=3%3Et.length%3F'':t%2Cc=c.replace(%2F%5C%2Famp%5C%2F%3F%24%2F%2C'')%2C(i!==t%7C%7Ce!==c)%26%26confirm('Update%20history%20and%20copy%20cleaned%20URL%20to%20clipboard%3F'))%7Bconst%20e=%60%24%7Blocation.protocol%7D%2F%2F%24%7Blocation.host%7D%24%7Bc%7D%24%7Bt%7D%24%7Blocation.hash%7D%60%3Bnavigator.clipboard.writeText(e)%2Chistory.replaceState(null%2C''%2Ce)%3Bconst%20i=window.open(e%2C'%5Fself'%2C'noreferrer')%3Bi%26%26(i.opener=null)%7D%7D)()%3Bvoid'2.1.0'
javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=location.pathname%2Ce=location.search%3Bif(3%3Ee.length%26%26!t.includes('%2Famp'))return%3Bconst%20i=location.hostname%2Cs=new%20URLSearchParams(e)%2Ca=%5B'assetType'%2C'elqTrack'%2C'mkt%5Ftok'%2C'originalReferer'%2C'referrer'%2C'terminal%5Fid'%2C'trk'%2C'trkCampaign'%2C'trkInfo'%2C'anid'%2C'assetid'%2C'campaignid'%2C'eid'%2C'gclid'%2C'recipientid'%2C'siteid'%2C'sib%5Fcuid'%2C'sib%5Fsid'%2C'%5Fbta%5Ftid'%2C'%5Fbta%5Fc'%2C'%5F%5Fs'%2C'fbclid'%2C'hrc'%2C'igsh'%2C'igshid'%2C'refsrc'%2C'%5Fgl'%2C'gclsrc'%2C'srsltid'%2C'%5Fhsenc'%2C'%5Fhsmi'%2C'%5F%5Fhsfp'%2C'%5F%5Fhssc'%2C'%5F%5Fhstc'%2C'cm%5Fmmc'%2C'cm%5Fre'%2C'cm%5Fsp'%2C'manual%5Fcm%5Fmmc'%2C'%5Fke'%2C'%5Fkx'%2C'trk%5Fcontact'%2C'trk%5Fmsg'%2C'trk%5Fmodule'%2C'trk%5Fsid'%2C'mc%5Fcid'%2C'mc%5Feid'%2C'iesrc'%2C'msclkid'%2C'dclid'%2C'twclid'%2C'ttclid'%2C'oly%5Fenc%5Fid'%2C'oly%5Fanon%5Fid'%2C'epik'%2C'vero%5Fid'%5D%2C%5F=%5B'fb%5F'%2C'action%5F'%2C'ga%5F'%2C'utm%5F'%2C'hmb%5F'%2C'hsa%5F'%2C'mtm%5F'%2C'pk%5F'%2C'oly%5F'%2C'stm%5F'%5D%3B%2F%5C.aliexpress%5C.%5Ba-z%5D%7B2%2C3%7D%24%2F.test(i)%3F(a.push('algo%5Fevid'%2C'algo%5Fpvid'%2C'btsid'%2C'spm'%2C'scm'%2C'ws%5Fab%5Ftest')%2C%5F.push('aff%5F')):%2F(%7C%5C.)amazon%5C.com%24%2F.test(i)%3Fa.push('%5Fencoding'%2C'asc%5Fcampaign'%2C'asc%5Frefurl'%2C'asc%5Fsource'%2C'ascsubtag'%2C'content-id'%2C'crid'%2C'cv%5Fct%5Fcx'%2C'dib%5Ftag'%2C'dib'%2C'ie'%2C'language'%2C'linkCode'%2C'linkId'%2C'pd%5Frd%5Fi'%2C'pd%5Frd%5Fr'%2C'pd%5Frd%5Fw'%2C'pd%5Frd%5Fwg'%2C'pf%5Frd%5Fi'%2C'pf%5Frd%5Fm'%2C'pf%5Frd%5Fp'%2C'pf%5Frd%5Fr'%2C'pf%5Frd%5Fs'%2C'pf%5Frd%5Ft'%2C'pf'%2C'psc'%2C'qid'%2C'ref%5F'%2C'sprefix'%2C'sr'%2C'tag'%2C'th'):i.endsWith('.ebay.com')%7C%7C%2F%5C.ebay%5C.co%5C.%5Ba-z%5D%7B2%7D%24%2F.test(i)%3Fa.push('mkevt'%2C'mkcid'%2C'mkrid'%2C'campid'%2C'toolid'%2C'customid'%2C'norover'%2C'itm'%2C'amdata'):%2F(%5E%7C%5C.)google%5C.(com%7C%5Ba-z%5D%7B2%7D%7Ccom%3F%5C.%5Ba-z%5D%7B2%7D)%24%2F.test(i)%26%26t.startsWith('%2Fsearch')%3Fa.push('aqs'%2C'ei'%2C'gs%5Flp'%2C'gs%5Fssp'%2C'iflsig'%2C'sca%5Fesv'%2C'ved'%2C'oq'%2C'sa'%2C'uact'%2C'rlz'%2C'sxsrf'%2C'bih'%2C'biw'%2C'client'%2C'prmd'%2C'sclient'%2C'source'%2C'sourceid'%2C'ie'%2C'oe'):i.endsWith('.linkedin.com')%3Fa.push('li%5Ffat%5Fid'%2C'licu'%2C'lipi'%2C'midSig'%2C'midToken'%2C'refId'):i.endsWith('.target.com')%3Fa.push('afid'%2C'clkid'%2C'lnm'%2C'preselect'%2C'tref'):i.endsWith('.temu.com')%3F(a.push('%5Fbg%5Ffs'%2C'%5Fp%5Fjump%5Fid'%2C'%5Fp%5Frfs'%2C'refer%5Fpage%5Fid'%2C'refer%5Fpage%5Fname'%2C'refer%5Fpage%5Fsn')%2C%5F.push('%5Fx%5F')):i.endsWith('.tiktok.com')%7C%7C'tiktok.com'===i%3Fa.push('%5Fd'%2C'%5Fr'%2C'%5Ft'%2C'is%5Ffrom%5Fwebapp'%2C'preview%5Fpb'%2C'share%5Fapp%5Fname'%2C'share%5Fitem%5Fid'%2C'tt4d%5Ft'%2C'timestamp'%2C'u%5Fcode'%2C'user%5Fid'):%2F%5C.(twitter%7Cx)%5C.com%24%2F.test(i)%7C%7C%2F%5E(twitter%7Cx)%5C.com%24%2F.test(i)%3Fa.push('cn'%2C'ref%5Fsrc'%2C'ref%5Furl'%2C's'%2C't'):i.endsWith('.walmart.com')%3Fa.push('adsredirect'%2C'affiliates%5Fad%5Fid'%2C'athcpid'%2C'athpgid'%2C'athcgid'%2C'athmtid'%2C'athstid'%2C'athznid'%2C'athiession'%2C'athancid'%2C'athposb'%2C'athena'%2C'campaign%5Fid'%2C'wmlspartner'):(%2F(m%7Cwww)%5C.youtube%5C.com%24%2F.test(i)%7C%7C'youtu.be'===i%7C%7C'www.youtube-nocookie.com'===i)%26%26a.push('ac'%2C'annotation%5Fid'%2C'app'%2C'feature'%2C'gclid'%2C'kw'%2C'src%5Fvid')%3Bconst%20c=new%20Set(a.map(t=%3Et.toLowerCase()))%2Cr=t=%3E%7Bconst%20e=t.toLowerCase()%3Breturn%20c.has(e)%7C%7C%5F.some(t=%3Ee.startsWith(t))%7C%7C%2F%5Ecm%5Fmmca%5Cd%2B%24%2Fi.test(t)%7D%3Bfor(const%20t%20of%5B...s.keys()%5D)r(t)%26%26s.delete(t)%3Blet%20d=s.toString()%3Bd=d%3F'%3F'%2Bd:''%3Bconst%20o=t.replace(%2F%5C%2Famp%5C%2F%3F%24%2F%2C'')%3Bif((e!==d%7C%7Ct!==o)%26%26confirm('Update%20history%20and%20copy%20cleaned%20URL%20to%20clipboard%3F'))%7Bconst%20t=%60%24%7Blocation.protocol%7D%2F%2F%24%7Blocation.host%7D%24%7Bo%7D%24%7Bd%7D%24%7Blocation.hash%7D%60%3Bnavigator.clipboard.writeText(t)%2Chistory.replaceState(null%2C''%2Ct)%3Bconst%20e=window.open(t%2C'%5Fself'%2C'noreferrer')%3Be%26%26(e.opener=null)%7D%7D)()%3Bvoid'2.2.0'
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,5 @@
"test": "npm run build && npm run verify-build",
"verify-build": "node scripts/verify-build.js"
},
"version": "4.1.0"
"version": "4.1.1"
}
223 changes: 223 additions & 0 deletions scripts/test-utmstrip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
#!/usr/bin/env node
/* eslint no-plusplus: 0 */
// cSpell:words Bronto Klaviyo Listrak Omeda
/* cSpell: ignore afid amdata anid ascsubtag assetid athancid athcgid athcpid athiession athmtid athpgid athposb athstid athznid btsid campaignid campid clkid crid customid dclid epik evid fbclid gclid gclsrc hsenc hsfp hsmi hssc hstc iesrc iflsig igsh igshid licu lipi mkcid mkevt mkrid mmca msclkid prmd pvid recipientid sclient siteid sourceid sprefix srsltid sxsrf ttclid twclid uact wmlspartner youtu */
/**
* Test utmstrip.ts logic against test URL corpus
* Run: node scripts/test-utmstrip.js
*/
const testData = require('../tests/utmstrip-test-urls.json');

// Replicate the shouldDelete logic from utmstrip.ts
const universalExact = [
// Generic tracking
'assetType', 'elqTrack', 'mkt_tok', 'originalReferer', 'referrer',
'terminal_id', 'trk', 'trkCampaign', 'trkInfo',
// *id suffix params
'anid', 'assetid', 'campaignid', 'eid', 'gclid', 'recipientid', 'siteid',
// Brevo/Sendinblue
'sib_cuid', 'sib_sid',
// Bronto (Oracle)
'_bta_c', '_bta_tid',
// Drip
'__s',
// Facebook/Instagram
'fbclid', 'hrc', 'igsh', 'igshid', 'refsrc',
// Google Analytics
'_gl', 'gclsrc', 'srsltid',
// HubSpot
'_hsenc', '_hsmi', '__hsfp', '__hssc', '__hstc',
// IBM (non-numeric only)
'cm_mmc', 'cm_re', 'cm_sp', 'manual_cm_mmc',
// Klaviyo
'_ke', '_kx',
// Listrak
'trk_contact', 'trk_module', 'trk_msg', 'trk_sid',
// MailChimp
'mc_cid', 'mc_eid',
// Marketo
'iesrc',
// Microsoft & ad platform click IDs
'dclid', 'msclkid', 'ttclid', 'twclid',
// Omeda
'oly_anon_id', 'oly_enc_id',
// Pinterest
'epik',
// Vero
'vero_id'
];

const universalPrefixes = [
// Facebook: fb_action_ids, fb_action_types, fb_ref, fb_source & action_*
'action_', 'fb_',
// Google Analytics
'ga_', 'utm_',
// HubSpot
'hmb_', 'hsa_',
// Matomo
'mtm_', 'pk_',
// Sailthru email
'stm_',
// Omeda
'oly_'
];

const aliexpressExact = ['algo_evid', 'algo_pvid', 'btsid', 'scm', 'spm', 'ws_ab_test'];
const aliexpressPrefixes = ['aff_'];

const amazonExact = [
'_encoding', 'asc_campaign', 'asc_refurl', 'asc_source', 'ascsubtag',
'content-id', 'crid', 'cv_ct_cx', 'dib_tag', 'dib', 'ie', 'language',
'linkCode', 'linkId', 'pd_rd_i', 'pd_rd_r', 'pd_rd_w', 'pd_rd_wg', 'pf_rd_i',
'pf_rd_m', 'pf_rd_p', 'pf_rd_r', 'pf_rd_s', 'pf_rd_t', 'pf', 'psc', 'qid',
'ref_', 'sprefix', 'sr', 'tag', 'th'
];

const youtubeExact = ['ac', 'annotation_id', 'app', 'feature', 'gclid', 'kw', 'src_vid'];

// Host-specific: eBay
const ebayExact = [
'amdata', 'campid', 'customid', 'itm', 'mkcid', 'mkevt', 'mkrid', 'norover', 'toolid'
];

// Host-specific: LinkedIn
const linkedinExact = ['li_fat_id', 'licu', 'lipi', 'midSig', 'midToken', 'refId'];

// Host-specific: Google Search (only on /search path)
const googleSearchExact = [
// Session/tracking identifiers
'ei', 'ved', 'iflsig', 'sca_esv', 'aqs', 'gs_lp', 'gs_ssp',
// Query tracking
'oq', 'sa', 'uact',
// Distribution/client tracking
'rlz', 'sxsrf',
// Browser/client info (clutter)
'bih', 'biw', 'client', 'prmd', 'sclient', 'source', 'sourceid',
// Encoding params (clutter)
'ie', 'oe'
];

// Host-specific: Target
const targetExact = ['afid', 'clkid', 'lnm', 'tref', 'preselect'];

// Host-specific: Temu
const temuExact = [
'_bg_fs', '_p_jump_id', '_p_rfs', 'refer_page_id', 'refer_page_name', 'refer_page_sn'
];
const temuPrefixes = ['_x_'];

// Host-specific: TikTok
const tiktokExact = [
'_d', '_r', '_t', 'is_from_webapp', 'preview_pb', 'share_app_name', 'share_item_id',
'timestamp', 'tt4d_t', 'u_code', 'user_id',
];

// Host-specific: Twitter/X
const twitterExact = ['cn', 'ref_src', 'ref_url', 's', 't'];

// Host-specific: Walmart
const walmartExact = [
'adsredirect', 'affiliates_ad_id', 'athancid', 'athcgid', 'athcpid',
'athena', 'athiession', 'athmtid', 'athpgid', 'athposb', 'athstid',
'athznid', 'campaign_id', 'wmlspartner'
];


function stripUrl(inputUrl) {
const url = new URL(inputUrl);
const locSearch = url.search;
const locPath = url.pathname;

// Early exit if nothing to strip
if (locSearch.length < 3 && !locPath.includes('/amp')) {
return inputUrl;
}

const params = new URLSearchParams(locSearch);
const host = url.hostname;

// Build combined param lists based on host
const exactParams = [...universalExact];
const prefixParams = [...universalPrefixes];

if ((/\.aliexpress\.[a-z]{2,3}$/).test(host)) {
exactParams.push(...aliexpressExact);
prefixParams.push(...aliexpressPrefixes);
} else if ((/(|\.)amazon\.com$/).test(host)) {
exactParams.push(...amazonExact);
} else if (host.endsWith('.ebay.com') || (/\.ebay\.co\.[a-z]{2}$/).test(host)) {
exactParams.push(...ebayExact);
} else if ((/(^|\.)google\.(com|[a-z]{2}|com?\.[a-z]{2})$/).test(host) && locPath.startsWith('/search')) {
exactParams.push(...googleSearchExact);
} else if (host.endsWith('.linkedin.com')) {
exactParams.push(...linkedinExact);
} else if (host.endsWith('.target.com')) {
exactParams.push(...targetExact);
} else if (host.endsWith('.temu.com')) {
exactParams.push(...temuExact);
prefixParams.push(...temuPrefixes);
} else if (host.endsWith('.tiktok.com') || host === 'tiktok.com') {
exactParams.push(...tiktokExact);
} else if ((/\.(twitter|x)\.com$/).test(host) || (/^(twitter|x)\.com$/).test(host)) {
exactParams.push(...twitterExact);
} else if (host.endsWith('.walmart.com')) {
exactParams.push(...walmartExact);
} else if ((/(m|www)\.youtube\.com$/).test(host) ||
host === 'youtu.be' || host === 'www.youtube-nocookie.com') {
exactParams.push(...youtubeExact);
}

// Convert to lowercase Set for O(1) lookup
const exactSet = new Set(exactParams.map(p => p.toLowerCase()));

const shouldDelete = (key) => {
const k = key.toLowerCase();
if (exactSet.has(k)) {
return true;
} else if (prefixParams.some(p => k.startsWith(p))) {
return true;
} else if (/^cm_mmca\d+$/i.test(key)) {
return true;
}
return false;
};

for (const key of [...params.keys()]) {
if (shouldDelete(key)) {
params.delete(key);
}
}

let searchStr = params.toString();
searchStr = searchStr ? `?${searchStr}` : '';

const pathStr = url.pathname.replace(/\/amp\/?$/, '');

return `${url.protocol}//${url.host}${pathStr}${searchStr}${url.hash}`;
}

// Run tests
let passed = 0;
let failed = 0;

console.log('Testing UTMStrip refactored logic...\n');

for (const test of testData.testCases) {
const result = stripUrl(test.input);
const ok = result === test.expected;

if (ok) {
passed++;
console.log(`✓ ${test.name}`);
} else {
failed++;
console.log(`✗ ${test.name}`);
console.log(` Input: ${test.input}`);
console.log(` Expected: ${test.expected}`);
console.log(` Got: ${result}`);
console.log(` Note: ${test.note}`);
}
}

console.log(`\n${passed} passed, ${failed} failed out of ${testData.testCases.length} tests`);
process.exit(failed > 0 ? 1 : 0);
Loading
Loading