Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@analytics-debugger/ads-click-tracker

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@analytics-debugger/ads-click-tracker - npm Package Compare versions

Comparing version
0.0.1-beta.1
to
0.0.1-beta.2
+32
-24
dist/AdsClickTracker.esm.js

@@ -13,7 +13,10 @@ class AdsClickTracker {

};
options.clickIdConfigs.forEach(({ name, expirationMs = 2592e6, maxClicks = 100 }) => {
this.configs.set(name, { expires: expirationMs, max: maxClicks });
options.clickIdConfigs.forEach(({ name, ttl = 2592e6, maxClicks = 100 }) => {
this.configs.set(name, { expires: ttl, max: maxClicks });
this.clicks[name] = this.clicks[name] || [];
});
this.load();
if (typeof this.options.callbacks?.onLoaded === "function") {
this.options.callbacks.onLoaded(this.get(true));
}
this.checkUrl();

@@ -82,22 +85,17 @@ }

const referrer = typeof document !== "undefined" ? document.referrer : "";
if (!Array.isArray(this.clicks[source])) {
this.clicks[source] = [];
}
const clickData = {
value,
timestamp: now,
expiresAt: now + expiresMs,
landing,
referrer
};
this.clicks[source] = this.clicks[source] || [];
const existingIndex = this.clicks[source].findIndex((c) => c.value === value);
if (existingIndex >= 0) {
this.clicks[source][existingIndex] = {
value,
timestamp: now,
expiresAt: now + expiresMs,
landing,
referrer
};
this.clicks[source][existingIndex] = clickData;
typeof this.options.callbacks?.onUpdatedClickId === "function" && this.options.callbacks.onUpdatedClickId(clickData);
} else {
this.clicks[source].push({
value,
timestamp: now,
expiresAt: now + expiresMs,
landing,
referrer
});
this.clicks[source].push(clickData);
typeof this.options.callbacks?.onNewClickId === "function" && this.options.callbacks.onNewClickId(clickData);
}

@@ -111,8 +109,18 @@ this.save();

}
get(source) {
const now = Date.now();
if (source) {
return Array.isArray(this.clicks[source]) ? this.clicks[source].filter((c) => c && c.expiresAt > now) : [];
get(latest) {
if (!latest || latest !== true) {
return this.clicks;
}
return Object.values(this.clicks).flat().filter((c) => c && c.expiresAt > now);
const getLatestClick = (clicks = []) => {
if (!clicks.length)
return [];
const latest2 = clicks.reduce((newest, current) => current.timestamp > newest.timestamp ? current : newest, clicks[0]);
return [latest2];
};
const result = {};
const allSources = Array.from(this.configs.keys());
allSources.forEach((sourceName) => {
result[sourceName] = getLatestClick(this.clicks[sourceName]);
});
return result;
}

@@ -119,0 +127,0 @@ clear(source) {

@@ -1,1 +0,1 @@

class n{clicks={};configs=new Map;options;constructor(s){this.options={storageKey:"_act_",debug:!1,checkHash:!0,decodeValues:!0,...s},s.clickIdConfigs.forEach(({name:t,expirationMs:i=2592e6,maxClicks:e=100})=>{this.configs.set(t,{expires:i,max:e}),this.clicks[t]=this.clicks[t]||[]}),this.load(),this.checkUrl()}log(s){this.options.debug&&console.info(`[Tracker] ${s}`)}load(){try{const s=localStorage.getItem(this.options.storageKey);if(s){const t=JSON.parse(s);this.configs.forEach((i,e)=>{this.clicks[e]=Array.isArray(t[e])?t[e]:[]})}this.cleanup()}catch(s){this.log(`Load error: ${s}`)}}save(){try{localStorage.setItem(this.options.storageKey,JSON.stringify(this.clicks))}catch(s){this.log(`Save error: ${s}`)}}cleanup(){const s=Date.now();Object.keys(this.clicks).forEach(t=>{Array.isArray(this.clicks[t])||(this.clicks[t]=[]);const i=this.configs.get(t);this.clicks[t]=this.clicks[t].filter(e=>e&&e.expiresAt>s).slice(-(i?.max||100))})}checkUrl(){if(!(typeof window>"u"))try{const s=new URL(window.location.href);this.processParams(s.searchParams),this.options.checkHash&&s.hash&&this.processParams(new URLSearchParams(s.hash.substring(1)))}catch(s){this.log(`URL parse error: ${s}`)}}processParams(s){this.configs.forEach((t,i)=>{const e=s.get(i);e&&this.recordClick(i,this.options.decodeValues?decodeURIComponent(e):e,t.expires)})}recordClick(s,t,i){const e=Date.now(),r=typeof window<"u"?window.location.href:"",o=typeof document<"u"?document.referrer:"";Array.isArray(this.clicks[s])||(this.clicks[s]=[]);const a=this.clicks[s].findIndex(l=>l.value===t);a>=0?this.clicks[s][a]={value:t,timestamp:e,expiresAt:e+i,landing:r,referrer:o}:this.clicks[s].push({value:t,timestamp:e,expiresAt:e+i,landing:r,referrer:o}),this.save()}track(s,t,i){const e=this.configs.get(s)||{expires:i||2592e6};this.recordClick(s,t,e.expires)}get(s){const t=Date.now();return s?Array.isArray(this.clicks[s])?this.clicks[s].filter(i=>i&&i.expiresAt>t):[]:Object.values(this.clicks).flat().filter(i=>i&&i.expiresAt>t)}clear(s){s?this.clicks[s]=[]:Object.keys(this.clicks).forEach(t=>{this.clicks[t]=[]}),this.save()}}let h;function k(c){return h||(h=new n(c))}export{k as default};
class k{clicks={};configs=new Map;options;constructor(s){this.options={storageKey:"_act_",debug:!1,checkHash:!0,decodeValues:!0,...s},s.clickIdConfigs.forEach(({name:c,ttl:i=2592e6,maxClicks:t=100})=>{this.configs.set(c,{expires:i,max:t}),this.clicks[c]=this.clicks[c]||[]}),this.load(),typeof this.options.callbacks?.onLoaded=="function"&&this.options.callbacks.onLoaded(this.get(!0)),this.checkUrl()}log(s){this.options.debug&&console.info(`[Tracker] ${s}`)}load(){try{const s=localStorage.getItem(this.options.storageKey);if(s){const c=JSON.parse(s);this.configs.forEach((i,t)=>{this.clicks[t]=Array.isArray(c[t])?c[t]:[]})}this.cleanup()}catch(s){this.log(`Load error: ${s}`)}}save(){try{localStorage.setItem(this.options.storageKey,JSON.stringify(this.clicks))}catch(s){this.log(`Save error: ${s}`)}}cleanup(){const s=Date.now();Object.keys(this.clicks).forEach(c=>{Array.isArray(this.clicks[c])||(this.clicks[c]=[]);const i=this.configs.get(c);this.clicks[c]=this.clicks[c].filter(t=>t&&t.expiresAt>s).slice(-(i?.max||100))})}checkUrl(){if(!(typeof window>"u"))try{const s=new URL(window.location.href);this.processParams(s.searchParams),this.options.checkHash&&s.hash&&this.processParams(new URLSearchParams(s.hash.substring(1)))}catch(s){this.log(`URL parse error: ${s}`)}}processParams(s){this.configs.forEach((c,i)=>{const t=s.get(i);t&&this.recordClick(i,this.options.decodeValues?decodeURIComponent(t):t,c.expires)})}recordClick(s,c,i){const t=Date.now(),e=typeof window<"u"?window.location.href:"",o=typeof document<"u"?document.referrer:"",r={value:c,timestamp:t,expiresAt:t+i,landing:e,referrer:o};this.clicks[s]=this.clicks[s]||[];const n=this.clicks[s].findIndex(l=>l.value===c);n>=0?(this.clicks[s][n]=r,typeof this.options.callbacks?.onUpdatedClickId=="function"&&this.options.callbacks.onUpdatedClickId(r)):(this.clicks[s].push(r),typeof this.options.callbacks?.onNewClickId=="function"&&this.options.callbacks.onNewClickId(r)),this.save()}track(s,c,i){const t=this.configs.get(s)||{expires:i||2592e6};this.recordClick(s,c,t.expires)}get(s){if(!s||s!==!0)return this.clicks;const c=(t=[])=>t.length?[t.reduce((e,o)=>o.timestamp>e.timestamp?o:e,t[0])]:[],i={};return Array.from(this.configs.keys()).forEach(t=>{i[t]=c(this.clicks[t])}),i}clear(s){s?this.clicks[s]=[]:Object.keys(this.clicks).forEach(c=>{this.clicks[c]=[]}),this.save()}}let h;function p(a){return h||(h=new k(a))}export{p as default};

@@ -16,7 +16,10 @@ var AdsClickTracker = (function () {

};
options.clickIdConfigs.forEach(({ name, expirationMs = 2592e6, maxClicks = 100 }) => {
this.configs.set(name, { expires: expirationMs, max: maxClicks });
options.clickIdConfigs.forEach(({ name, ttl = 2592e6, maxClicks = 100 }) => {
this.configs.set(name, { expires: ttl, max: maxClicks });
this.clicks[name] = this.clicks[name] || [];
});
this.load();
if (typeof this.options.callbacks?.onLoaded === "function") {
this.options.callbacks.onLoaded(this.get(true));
}
this.checkUrl();

@@ -85,22 +88,17 @@ }

const referrer = typeof document !== "undefined" ? document.referrer : "";
if (!Array.isArray(this.clicks[source])) {
this.clicks[source] = [];
}
const clickData = {
value,
timestamp: now,
expiresAt: now + expiresMs,
landing,
referrer
};
this.clicks[source] = this.clicks[source] || [];
const existingIndex = this.clicks[source].findIndex((c) => c.value === value);
if (existingIndex >= 0) {
this.clicks[source][existingIndex] = {
value,
timestamp: now,
expiresAt: now + expiresMs,
landing,
referrer
};
this.clicks[source][existingIndex] = clickData;
typeof this.options.callbacks?.onUpdatedClickId === "function" && this.options.callbacks.onUpdatedClickId(clickData);
} else {
this.clicks[source].push({
value,
timestamp: now,
expiresAt: now + expiresMs,
landing,
referrer
});
this.clicks[source].push(clickData);
typeof this.options.callbacks?.onNewClickId === "function" && this.options.callbacks.onNewClickId(clickData);
}

@@ -114,8 +112,18 @@ this.save();

}
get(source) {
const now = Date.now();
if (source) {
return Array.isArray(this.clicks[source]) ? this.clicks[source].filter((c) => c && c.expiresAt > now) : [];
get(latest) {
if (!latest || latest !== true) {
return this.clicks;
}
return Object.values(this.clicks).flat().filter((c) => c && c.expiresAt > now);
const getLatestClick = (clicks = []) => {
if (!clicks.length)
return [];
const latest2 = clicks.reduce((newest, current) => current.timestamp > newest.timestamp ? current : newest, clicks[0]);
return [latest2];
};
const result = {};
const allSources = Array.from(this.configs.keys());
allSources.forEach((sourceName) => {
result[sourceName] = getLatestClick(this.clicks[sourceName]);
});
return result;
}

@@ -122,0 +130,0 @@ clear(source) {

@@ -1,1 +0,1 @@

var AdsClickTracker=function(){"use strict";class n{clicks={};configs=new Map;options;constructor(s){this.options={storageKey:"_act_",debug:!1,checkHash:!0,decodeValues:!0,...s},s.clickIdConfigs.forEach(({name:t,expirationMs:i=2592e6,maxClicks:c=100})=>{this.configs.set(t,{expires:i,max:c}),this.clicks[t]=this.clicks[t]||[]}),this.load(),this.checkUrl()}log(s){this.options.debug&&console.info(`[Tracker] ${s}`)}load(){try{const s=localStorage.getItem(this.options.storageKey);if(s){const t=JSON.parse(s);this.configs.forEach((i,c)=>{this.clicks[c]=Array.isArray(t[c])?t[c]:[]})}this.cleanup()}catch(s){this.log(`Load error: ${s}`)}}save(){try{localStorage.setItem(this.options.storageKey,JSON.stringify(this.clicks))}catch(s){this.log(`Save error: ${s}`)}}cleanup(){const s=Date.now();Object.keys(this.clicks).forEach(t=>{Array.isArray(this.clicks[t])||(this.clicks[t]=[]);const i=this.configs.get(t);this.clicks[t]=this.clicks[t].filter(c=>c&&c.expiresAt>s).slice(-(i?.max||100))})}checkUrl(){if(!(typeof window>"u"))try{const s=new URL(window.location.href);this.processParams(s.searchParams),this.options.checkHash&&s.hash&&this.processParams(new URLSearchParams(s.hash.substring(1)))}catch(s){this.log(`URL parse error: ${s}`)}}processParams(s){this.configs.forEach((t,i)=>{const c=s.get(i);c&&this.recordClick(i,this.options.decodeValues?decodeURIComponent(c):c,t.expires)})}recordClick(s,t,i){const c=Date.now(),o=typeof window<"u"?window.location.href:"",a=typeof document<"u"?document.referrer:"";Array.isArray(this.clicks[s])||(this.clicks[s]=[]);const h=this.clicks[s].findIndex(k=>k.value===t);h>=0?this.clicks[s][h]={value:t,timestamp:c,expiresAt:c+i,landing:o,referrer:a}:this.clicks[s].push({value:t,timestamp:c,expiresAt:c+i,landing:o,referrer:a}),this.save()}track(s,t,i){const c=this.configs.get(s)||{expires:i||2592e6};this.recordClick(s,t,c.expires)}get(s){const t=Date.now();return s?Array.isArray(this.clicks[s])?this.clicks[s].filter(i=>i&&i.expiresAt>t):[]:Object.values(this.clicks).flat().filter(i=>i&&i.expiresAt>t)}clear(s){s?this.clicks[s]=[]:Object.keys(this.clicks).forEach(t=>{this.clicks[t]=[]}),this.save()}}let e;function l(r){return e||(e=new n(r))}return l}();
var AdsClickTracker=function(){"use strict";class l{clicks={};configs=new Map;options;constructor(s){this.options={storageKey:"_act_",debug:!1,checkHash:!0,decodeValues:!0,...s},s.clickIdConfigs.forEach(({name:c,ttl:i=2592e6,maxClicks:t=100})=>{this.configs.set(c,{expires:i,max:t}),this.clicks[c]=this.clicks[c]||[]}),this.load(),typeof this.options.callbacks?.onLoaded=="function"&&this.options.callbacks.onLoaded(this.get(!0)),this.checkUrl()}log(s){this.options.debug&&console.info(`[Tracker] ${s}`)}load(){try{const s=localStorage.getItem(this.options.storageKey);if(s){const c=JSON.parse(s);this.configs.forEach((i,t)=>{this.clicks[t]=Array.isArray(c[t])?c[t]:[]})}this.cleanup()}catch(s){this.log(`Load error: ${s}`)}}save(){try{localStorage.setItem(this.options.storageKey,JSON.stringify(this.clicks))}catch(s){this.log(`Save error: ${s}`)}}cleanup(){const s=Date.now();Object.keys(this.clicks).forEach(c=>{Array.isArray(this.clicks[c])||(this.clicks[c]=[]);const i=this.configs.get(c);this.clicks[c]=this.clicks[c].filter(t=>t&&t.expiresAt>s).slice(-(i?.max||100))})}checkUrl(){if(!(typeof window>"u"))try{const s=new URL(window.location.href);this.processParams(s.searchParams),this.options.checkHash&&s.hash&&this.processParams(new URLSearchParams(s.hash.substring(1)))}catch(s){this.log(`URL parse error: ${s}`)}}processParams(s){this.configs.forEach((c,i)=>{const t=s.get(i);t&&this.recordClick(i,this.options.decodeValues?decodeURIComponent(t):t,c.expires)})}recordClick(s,c,i){const t=Date.now(),e=typeof window<"u"?window.location.href:"",o=typeof document<"u"?document.referrer:"",r={value:c,timestamp:t,expiresAt:t+i,landing:e,referrer:o};this.clicks[s]=this.clicks[s]||[];const h=this.clicks[s].findIndex(p=>p.value===c);h>=0?(this.clicks[s][h]=r,typeof this.options.callbacks?.onUpdatedClickId=="function"&&this.options.callbacks.onUpdatedClickId(r)):(this.clicks[s].push(r),typeof this.options.callbacks?.onNewClickId=="function"&&this.options.callbacks.onNewClickId(r)),this.save()}track(s,c,i){const t=this.configs.get(s)||{expires:i||2592e6};this.recordClick(s,c,t.expires)}get(s){if(!s||s!==!0)return this.clicks;const c=(t=[])=>t.length?[t.reduce((e,o)=>o.timestamp>e.timestamp?o:e,t[0])]:[],i={};return Array.from(this.configs.keys()).forEach(t=>{i[t]=c(this.clicks[t])}),i}clear(s){s?this.clicks[s]=[]:Object.keys(this.clicks).forEach(c=>{this.clicks[c]=[]}),this.save()}}let a;function k(n){return a||(a=new l(n))}return k}();

@@ -19,7 +19,10 @@ (function (global, factory) {

};
options.clickIdConfigs.forEach(({ name, expirationMs = 2592e6, maxClicks = 100 }) => {
this.configs.set(name, { expires: expirationMs, max: maxClicks });
options.clickIdConfigs.forEach(({ name, ttl = 2592e6, maxClicks = 100 }) => {
this.configs.set(name, { expires: ttl, max: maxClicks });
this.clicks[name] = this.clicks[name] || [];
});
this.load();
if (typeof this.options.callbacks?.onLoaded === "function") {
this.options.callbacks.onLoaded(this.get(true));
}
this.checkUrl();

@@ -88,22 +91,17 @@ }

const referrer = typeof document !== "undefined" ? document.referrer : "";
if (!Array.isArray(this.clicks[source])) {
this.clicks[source] = [];
}
const clickData = {
value,
timestamp: now,
expiresAt: now + expiresMs,
landing,
referrer
};
this.clicks[source] = this.clicks[source] || [];
const existingIndex = this.clicks[source].findIndex((c) => c.value === value);
if (existingIndex >= 0) {
this.clicks[source][existingIndex] = {
value,
timestamp: now,
expiresAt: now + expiresMs,
landing,
referrer
};
this.clicks[source][existingIndex] = clickData;
typeof this.options.callbacks?.onUpdatedClickId === "function" && this.options.callbacks.onUpdatedClickId(clickData);
} else {
this.clicks[source].push({
value,
timestamp: now,
expiresAt: now + expiresMs,
landing,
referrer
});
this.clicks[source].push(clickData);
typeof this.options.callbacks?.onNewClickId === "function" && this.options.callbacks.onNewClickId(clickData);
}

@@ -117,8 +115,18 @@ this.save();

}
get(source) {
const now = Date.now();
if (source) {
return Array.isArray(this.clicks[source]) ? this.clicks[source].filter((c) => c && c.expiresAt > now) : [];
get(latest) {
if (!latest || latest !== true) {
return this.clicks;
}
return Object.values(this.clicks).flat().filter((c) => c && c.expiresAt > now);
const getLatestClick = (clicks = []) => {
if (!clicks.length)
return [];
const latest2 = clicks.reduce((newest, current) => current.timestamp > newest.timestamp ? current : newest, clicks[0]);
return [latest2];
};
const result = {};
const allSources = Array.from(this.configs.keys());
allSources.forEach((sourceName) => {
result[sourceName] = getLatestClick(this.clicks[sourceName]);
});
return result;
}

@@ -125,0 +133,0 @@ clear(source) {

@@ -1,1 +0,1 @@

(function(r,c){typeof exports=="object"&&typeof module<"u"?module.exports=c():typeof define=="function"&&define.amd?define(c):(r=typeof globalThis<"u"?globalThis:r||self,r.AdsClickTracker=c())})(void 0,function(){"use strict";class r{clicks={};configs=new Map;options;constructor(s){this.options={storageKey:"_act_",debug:!1,checkHash:!0,decodeValues:!0,...s},s.clickIdConfigs.forEach(({name:e,expirationMs:t=2592e6,maxClicks:i=100})=>{this.configs.set(e,{expires:t,max:i}),this.clicks[e]=this.clicks[e]||[]}),this.load(),this.checkUrl()}log(s){this.options.debug&&console.info(`[Tracker] ${s}`)}load(){try{const s=localStorage.getItem(this.options.storageKey);if(s){const e=JSON.parse(s);this.configs.forEach((t,i)=>{this.clicks[i]=Array.isArray(e[i])?e[i]:[]})}this.cleanup()}catch(s){this.log(`Load error: ${s}`)}}save(){try{localStorage.setItem(this.options.storageKey,JSON.stringify(this.clicks))}catch(s){this.log(`Save error: ${s}`)}}cleanup(){const s=Date.now();Object.keys(this.clicks).forEach(e=>{Array.isArray(this.clicks[e])||(this.clicks[e]=[]);const t=this.configs.get(e);this.clicks[e]=this.clicks[e].filter(i=>i&&i.expiresAt>s).slice(-(t?.max||100))})}checkUrl(){if(!(typeof window>"u"))try{const s=new URL(window.location.href);this.processParams(s.searchParams),this.options.checkHash&&s.hash&&this.processParams(new URLSearchParams(s.hash.substring(1)))}catch(s){this.log(`URL parse error: ${s}`)}}processParams(s){this.configs.forEach((e,t)=>{const i=s.get(t);i&&this.recordClick(t,this.options.decodeValues?decodeURIComponent(i):i,e.expires)})}recordClick(s,e,t){const i=Date.now(),a=typeof window<"u"?window.location.href:"",n=typeof document<"u"?document.referrer:"";Array.isArray(this.clicks[s])||(this.clicks[s]=[]);const h=this.clicks[s].findIndex(f=>f.value===e);h>=0?this.clicks[s][h]={value:e,timestamp:i,expiresAt:i+t,landing:a,referrer:n}:this.clicks[s].push({value:e,timestamp:i,expiresAt:i+t,landing:a,referrer:n}),this.save()}track(s,e,t){const i=this.configs.get(s)||{expires:t||2592e6};this.recordClick(s,e,i.expires)}get(s){const e=Date.now();return s?Array.isArray(this.clicks[s])?this.clicks[s].filter(t=>t&&t.expiresAt>e):[]:Object.values(this.clicks).flat().filter(t=>t&&t.expiresAt>e)}clear(s){s?this.clicks[s]=[]:Object.keys(this.clicks).forEach(e=>{this.clicks[e]=[]}),this.save()}}let c;function l(o){return c||(c=new r(o))}return l});
(function(o,c){typeof exports=="object"&&typeof module<"u"?module.exports=c():typeof define=="function"&&define.amd?define(c):(o=typeof globalThis<"u"?globalThis:o||self,o.AdsClickTracker=c())})(void 0,function(){"use strict";class o{clicks={};configs=new Map;options;constructor(s){this.options={storageKey:"_act_",debug:!1,checkHash:!0,decodeValues:!0,...s},s.clickIdConfigs.forEach(({name:t,ttl:i=2592e6,maxClicks:e=100})=>{this.configs.set(t,{expires:i,max:e}),this.clicks[t]=this.clicks[t]||[]}),this.load(),typeof this.options.callbacks?.onLoaded=="function"&&this.options.callbacks.onLoaded(this.get(!0)),this.checkUrl()}log(s){this.options.debug&&console.info(`[Tracker] ${s}`)}load(){try{const s=localStorage.getItem(this.options.storageKey);if(s){const t=JSON.parse(s);this.configs.forEach((i,e)=>{this.clicks[e]=Array.isArray(t[e])?t[e]:[]})}this.cleanup()}catch(s){this.log(`Load error: ${s}`)}}save(){try{localStorage.setItem(this.options.storageKey,JSON.stringify(this.clicks))}catch(s){this.log(`Save error: ${s}`)}}cleanup(){const s=Date.now();Object.keys(this.clicks).forEach(t=>{Array.isArray(this.clicks[t])||(this.clicks[t]=[]);const i=this.configs.get(t);this.clicks[t]=this.clicks[t].filter(e=>e&&e.expiresAt>s).slice(-(i?.max||100))})}checkUrl(){if(!(typeof window>"u"))try{const s=new URL(window.location.href);this.processParams(s.searchParams),this.options.checkHash&&s.hash&&this.processParams(new URLSearchParams(s.hash.substring(1)))}catch(s){this.log(`URL parse error: ${s}`)}}processParams(s){this.configs.forEach((t,i)=>{const e=s.get(i);e&&this.recordClick(i,this.options.decodeValues?decodeURIComponent(e):e,t.expires)})}recordClick(s,t,i){const e=Date.now(),r=typeof window<"u"?window.location.href:"",n=typeof document<"u"?document.referrer:"",a={value:t,timestamp:e,expiresAt:e+i,landing:r,referrer:n};this.clicks[s]=this.clicks[s]||[];const l=this.clicks[s].findIndex(f=>f.value===t);l>=0?(this.clicks[s][l]=a,typeof this.options.callbacks?.onUpdatedClickId=="function"&&this.options.callbacks.onUpdatedClickId(a)):(this.clicks[s].push(a),typeof this.options.callbacks?.onNewClickId=="function"&&this.options.callbacks.onNewClickId(a)),this.save()}track(s,t,i){const e=this.configs.get(s)||{expires:i||2592e6};this.recordClick(s,t,e.expires)}get(s){if(!s||s!==!0)return this.clicks;const t=(e=[])=>e.length?[e.reduce((r,n)=>n.timestamp>r.timestamp?n:r,e[0])]:[],i={};return Array.from(this.configs.keys()).forEach(e=>{i[e]=t(this.clicks[e])}),i}clear(s){s?this.clicks[s]=[]:Object.keys(this.clicks).forEach(t=>{this.clicks[t]=[]}),this.save()}}let c;function d(h){return c||(c=new o(h))}return d});
interface ClickConfig {
name: string;
expirationMs?: number;
ttl?: number;
maxClicks?: number;

@@ -12,2 +12,7 @@ }

decodeValues?: boolean;
callbacks?: {
onLoaded?: (clicks: Record<string, ClickData | ClickData[]>) => void;
onNewClickId?: (click: ClickData) => void;
onUpdatedClickId?: (click: ClickData) => void;
};
}

@@ -34,3 +39,3 @@ interface ClickData {

track(source: string, value: string, expiresMs?: number): void;
get(source?: string): ClickData[];
get(latest?: boolean): Record<string, ClickData[]>;
clear(source?: string): void;

@@ -37,0 +42,0 @@ }

{
"name": "@analytics-debugger/ads-click-tracker",
"type": "module",
"version": "0.0.1-beta.1",
"version": "0.0.1-beta.2",
"description": "Library to keep track of clickIds from different advertising platforms",

@@ -6,0 +6,0 @@ "author": "David Vallejo <thyngster@gmail.com>",

+129
-108
# 🎯 Ads Click Tracker
[![npm version](https://img.shields.io/npm/v/ads-click-tracker)](https://www.npmjs.com/package/ads-click-tracker)
[![bundle size](https://img.shields.io/bundlephobia/minzip/ads-click-tracker)](https://bundlephobia.com/package/ads-click-tracker)
[![license](https://img.shields.io/npm/l/ads-click-tracker)](LICENSE)
[![Downloads](https://img.shields.io/npm/dm/ads-click-tracker)](https://www.npmjs.com/package/ads-click-tracker)
[![npm version](https://img.shields.io/npm/v/@analytics-debugger/ads-click-tracker)](https://www.npmjs.com/package/@analytics-debugger/ads-click-tracker)
[![bundle size](https://bundlephobia.com/package/@analytics-debugger/ads-click-tracker)](https://bundlephobia.com/package/@analytics-debugger/ads-click-tracker@latest)
[![license](https://img.shields.io/npm/l/@analytics-debugger/ads-click-tracker)](LICENSE)
[![Downloads](https://img.shields.io/npm/dm/@analytics-debugger/ads-click-tracker)](https://www.npmjs.com/package/@analytics-debugger/ads-click-tracker)
## 📖 Overview
# @analytics-debugger/ads-click-tracker
Ads Click Tracker is a powerful, lightweight solution for marketing attribution tracking that simplifies the complexity of capturing and managing click data across multiple advertising platforms.
A lightweight, client-side library for tracking and managing ad click IDs across user sessions.
## 🌟 Key Features
## Features

@@ -33,2 +33,5 @@ - **💡 Comprehensive Attribution Tracking**

- **🪝 Event callbacks**
- For onLoad, New Click Id found, Updated ClickID
- **🚀 Developer-Friendly**

@@ -40,157 +43,175 @@ - Zero dependencies

## 🔧 Installation
## Installation
```bash
# npm
npm install ads-click-tracker
# Yarn
yarn add ads-click-tracker
# pnpm
pnpm add ads-click-tracker
npm install @analytics-debugger/ads-click-tracker
# or
yarn add @analytics-debugger/ads-click-tracker
```
## 💻 Quick Start
## Quick Start
### Basic Usage
```javascript
import { initTracker } from 'ads-click-tracker'
import AdsClickTracker from '@analytics-debugger/ads-click-tracker'
// Initialize with multiple click ID configurations
const tracker = initTracker({
// Initialize the tracker
const tracker = AdsClickTracker({
debug: true,
clickIdConfigs: [
{ name: 'gclid', expirationMs: 30 * 24 * 60 * 60 * 1000 }, // 30 days for Google Ads
{ name: 'fbclid', maxClicks: 50 } // 50 clicks limit for Facebook
{ name: 'gclid', ttl: 30 * 24 * 60 * 60 * 1000, maxClicks: 100 }, // Google Ads (30 days)
{ name: 'fbclid', ttl: 7 * 24 * 60 * 60 * 1000, maxClicks: 50 }, // Facebook (7 days)
{ name: 'ttclid', ttl: 14 * 24 * 60 * 60 * 1000, maxClicks: 50 }, // TikTok (14 days)
{ name: 'msclkid', ttl: 30 * 24 * 60 * 60 * 1000, maxClicks: 100 } // Microsoft (30 days)
],
debug: true // Enable console logging
callbacks: {
onLoaded: (clicks) => {
console.log('Tracker initialized with clicks:', clicks)
},
onNewClickId: (click) => {
console.log('New click ID detected:', click)
},
onUpdatedClickId: (click) => {
console.log('Click ID updated:', click)
}
}
})
// Get all tracked clicks
const allClicks = tracker.get()
// Get only the latest click for each source
const latestClicks = tracker.get(true)
// Manually track a click
tracker.trackClick('affiliate', 'ref123', 24 * 60 * 60 * 1000) // 1 day expiration
tracker.track('gclid', 'EAIaIQobChMIh76p9Y3', 2592000000) // value, expiration in ms
// Retrieve tracked clicks
const googleClicks = tracker.getClicks('gclid')
// Clear clicks for a specific source
tracker.clear('fbclid')
// Clear all clicks
tracker.clear()
```
## 📊 Click Data Structure
## API Reference
Each tracked click includes:
### Initialization
```typescript
interface TrackedClick {
value: string // Click ID (e.g., "abc123")
timestamp: number // Recording timestamp (milliseconds)
expiresAt: number // Expiration timestamp (milliseconds)
landing: string // Landing page URL
referrer: string // Referring URL
class AdsClickTracker {
constructor(options: TrackerOptions)
// ... other methods
}
```
## ⚙️ Configuration Options
#### TrackerOptions
### `initTracker(options)`
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `storageKey` | string | `'_act_'` | Key used for localStorage |
| `clickIdConfigs` | ClickConfig[] | *required* | Array of click ID configurations |
| `debug` | boolean | `false` | Enable debug logging |
| `checkHash` | boolean | `true` | Check hash fragment for click IDs |
| `decodeValues` | boolean | `true` | URL decode click ID values |
| `callbacks` | object | `{}` | Callback functions |
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `storageKey` | `string` | Custom storage key in localStorage | `'_ads_clicks'` |
| `clickIdConfigs` | `ClickConfig[]` | Click ID tracking configurations | `[]` |
| `debug` | `boolean` | Enable debug logging | `false` |
#### ClickConfig
### `ClickConfig`
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `name` | string | *required* | Parameter name to track (e.g., 'gclid') |
| `ttl` | number | `2592000000` (30 days) | Time in milliseconds before click expires |
| `maxClicks` | number | `100` | Maximum number of clicks to store per source |
| Property | Type | Description |
|----------|------|-------------|
| `name` | `string` | Parameter name (e.g., 'gclid') |
| `expirationMs` | `number?` | Expiration time in milliseconds |
| `maxClicks` | `number?` | Maximum number of clicks to store |
### Methods
## 📚 API Reference
#### get(latest?: boolean): Record<string, ClickData[]>
### `trackClick(source, value, expirationMs?)`
Get all tracked clicks. If `latest` is `true`, returns only the most recent click for each source.
Manually track a click:
#### track(source: string, value: string, ttl?: number): void
```javascript
tracker.trackClick(
'campaign_id', // Source name
'summer_sale', // Click ID value
7 * 24 * 60 * 60 * 1000 // Optional 7-day expiration
)
```
Manually track a click ID.
### `getClicks(source?)`
#### clear(source?: string): void
Retrieve tracked clicks:
Clear clicks for a specific source or all sources if no source is provided.
```javascript
// Get all active clicks
const allClicks = tracker.getClicks()
### ClickData Structure
// Get clicks for a specific source
const fbClicks = tracker.getClicks('fbclid')
```typescript
interface ClickData {
value: string // Click ID value
timestamp: number // When the click was recorded
expiresAt: number // When the click data expires
landing: string // Landing page URL
referrer: string // Referrer URL
}
```
### `clear(source?)`
## Use Cases
Clear stored clicks:
### Attribution Tracking
Track which ad campaigns are driving conversions by capturing click IDs when users arrive on your site.
```javascript
// Clear Google clicks
tracker.clear('gclid')
// On your conversion/thank you page
import AdsClickTracker from '@analytics-debugger/ads-click-tracker'
// Clear all clicks
tracker.clear()
```
## 🚀 Advanced Examples
### Marketing Attribution Setup
```javascript
const marketingTracker = initTracker({
storageKey: 'marketing_clicks',
const tracker = AdsClickTracker({
clickIdConfigs: [
{ name: 'gclid', expirationMs: 30 * 24 * 60 * 60 * 1000 }, // Google (30 days)
{ name: 'fbclid', expirationMs: 7 * 24 * 60 * 60 * 1000 }, // Facebook (7 days)
{ name: 'utm_campaign', maxClicks: 200 } // Custom campaign tracking
{ name: 'gclid' },
{ name: 'fbclid' }
]
})
// Get the latest clicks to include in your conversion tracking
const latestClicks = tracker.get(true)
// Send this data to your analytics or CRM system
sendToAnalytics({
conversion: true,
value: 100,
adClicks: latestClicks
})
```
### React Integration
### Integration with Forms
Automatically include ad click data in your form submissions for attribution.
```javascript
import { initTracker } from 'ads-click-tracker'
import { useEffect } from 'react'
document.querySelector('#leadForm').addEventListener('submit', (event) => {
const tracker = initTracker({
clickIdConfigs: [
{ name: 'gclid' },
{ name: 'fbclid' },
{ name: 'ttclid' },
{ name: 'msclkid' }
]
})
function useClickTracker() {
useEffect(() => {
const tracker = initTracker({
clickIdConfigs: [{ name: 'campaign_id' }]
})
const latestClicks = tracker.get(true)
return () => tracker.clear()
}, [])
}
// Add hidden fields to your form with click data
Object.entries(latestClicks).forEach(([source, clicks]) => {
if (clicks.length > 0) {
const hiddenField = document.createElement('input')
hiddenField.type = 'hidden'
hiddenField.name = source
hiddenField.value = clicks[0].value
event.target.appendChild(hiddenField)
}
})
})
```
## 🌐 Browser Support
## Browser Support
- Chrome
- Firefox
- Safari
- Edge (latest versions)
AdsClickTracker works in all modern browsers that support localStorage and ES6 features. For older browsers, consider using a transpiler like Babel.
**Requirements:**
- localStorage support
- Modern browsers
## License
**Note:** Internet Explorer 11 is not supported
MIT
## 📜 License
MIT License
## 🤝 Contributing

@@ -197,0 +218,0 @@