@blinkdotnew/sdk 0.8.0 → 0.10.0

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/README.md CHANGED
@@ -580,16 +580,23 @@ blink.analytics.log('button_clicked', {
580
580
  })
581
581
 
582
582
  // All events automatically include:
583
- // - timestamp, project_id, user_id, session_id
583
+ // - timestamp, project_id, user_id, user_email, session_id
584
584
  // - pathname (current page), referrer, screen_width
585
585
  // - device/browser/OS info (parsed server-side)
586
+ // - channel detection (Organic Search, Social, Direct, etc.)
587
+ // - UTM parameters (source, medium, campaign, content, term)
588
+ // - UTM persistence for attribution tracking across sessions
586
589
 
587
590
  // Control analytics
588
591
  blink.analytics.disable()
589
592
  blink.analytics.enable()
590
593
  const isEnabled = blink.analytics.isEnabled()
591
594
 
595
+ // Clear attribution data (e.g., when user logs out)
596
+ blink.analytics.clearAttribution()
597
+
592
598
  // Features: Privacy-first, offline support, event batching, session management
599
+ // Attribution: UTM params persist across sessions for conversion tracking
593
600
  ```
594
601
 
595
602
  ### Realtime Operations
package/dist/index.d.mts CHANGED
@@ -257,10 +257,17 @@ interface AnalyticsEvent {
257
257
  type: string;
258
258
  timestamp?: string;
259
259
  user_id?: string | null;
260
+ user_email?: string | null;
260
261
  session_id?: string | null;
261
262
  pathname?: string | null;
262
263
  referrer?: string | null;
263
264
  screen_width?: number | null;
265
+ channel?: string | null;
266
+ utm_source?: string | null;
267
+ utm_medium?: string | null;
268
+ utm_campaign?: string | null;
269
+ utm_content?: string | null;
270
+ utm_term?: string | null;
264
271
  [key: string]: any;
265
272
  }
266
273
  interface BlinkAnalytics {
@@ -269,6 +276,8 @@ interface BlinkAnalytics {
269
276
  enable(): void;
270
277
  isEnabled(): boolean;
271
278
  setUserId(userId: string | null): void;
279
+ setUserEmail(email: string | null): void;
280
+ clearAttribution(): void;
272
281
  }
273
282
  declare class BlinkAnalyticsImpl implements BlinkAnalytics {
274
283
  private httpClient;
@@ -277,7 +286,10 @@ declare class BlinkAnalyticsImpl implements BlinkAnalytics {
277
286
  private timer;
278
287
  private enabled;
279
288
  private userId;
289
+ private userEmail;
280
290
  private hasTrackedPageview;
291
+ private utmParams;
292
+ private persistedAttribution;
281
293
  constructor(httpClient: HttpClient, projectId: string);
282
294
  /**
283
295
  * Log a custom analytics event
@@ -299,6 +311,14 @@ declare class BlinkAnalyticsImpl implements BlinkAnalytics {
299
311
  * Set the user ID for analytics events
300
312
  */
301
313
  setUserId(userId: string | null): void;
314
+ /**
315
+ * Set the user email for analytics events
316
+ */
317
+ setUserEmail(email: string | null): void;
318
+ /**
319
+ * Clear persisted attribution data
320
+ */
321
+ clearAttribution(): void;
302
322
  private buildEvent;
303
323
  private sanitizeData;
304
324
  private enqueue;
@@ -311,6 +331,10 @@ declare class BlinkAnalyticsImpl implements BlinkAnalytics {
311
331
  private trackPageview;
312
332
  private setupRouteChangeListener;
313
333
  private setupUnloadListener;
334
+ private captureUTMParams;
335
+ private loadPersistedAttribution;
336
+ private persistAttribution;
337
+ private detectChannel;
314
338
  }
315
339
 
316
340
  /**
package/dist/index.d.ts CHANGED
@@ -257,10 +257,17 @@ interface AnalyticsEvent {
257
257
  type: string;
258
258
  timestamp?: string;
259
259
  user_id?: string | null;
260
+ user_email?: string | null;
260
261
  session_id?: string | null;
261
262
  pathname?: string | null;
262
263
  referrer?: string | null;
263
264
  screen_width?: number | null;
265
+ channel?: string | null;
266
+ utm_source?: string | null;
267
+ utm_medium?: string | null;
268
+ utm_campaign?: string | null;
269
+ utm_content?: string | null;
270
+ utm_term?: string | null;
264
271
  [key: string]: any;
265
272
  }
266
273
  interface BlinkAnalytics {
@@ -269,6 +276,8 @@ interface BlinkAnalytics {
269
276
  enable(): void;
270
277
  isEnabled(): boolean;
271
278
  setUserId(userId: string | null): void;
279
+ setUserEmail(email: string | null): void;
280
+ clearAttribution(): void;
272
281
  }
273
282
  declare class BlinkAnalyticsImpl implements BlinkAnalytics {
274
283
  private httpClient;
@@ -277,7 +286,10 @@ declare class BlinkAnalyticsImpl implements BlinkAnalytics {
277
286
  private timer;
278
287
  private enabled;
279
288
  private userId;
289
+ private userEmail;
280
290
  private hasTrackedPageview;
291
+ private utmParams;
292
+ private persistedAttribution;
281
293
  constructor(httpClient: HttpClient, projectId: string);
282
294
  /**
283
295
  * Log a custom analytics event
@@ -299,6 +311,14 @@ declare class BlinkAnalyticsImpl implements BlinkAnalytics {
299
311
  * Set the user ID for analytics events
300
312
  */
301
313
  setUserId(userId: string | null): void;
314
+ /**
315
+ * Set the user email for analytics events
316
+ */
317
+ setUserEmail(email: string | null): void;
318
+ /**
319
+ * Clear persisted attribution data
320
+ */
321
+ clearAttribution(): void;
302
322
  private buildEvent;
303
323
  private sanitizeData;
304
324
  private enqueue;
@@ -311,6 +331,10 @@ declare class BlinkAnalyticsImpl implements BlinkAnalytics {
311
331
  private trackPageview;
312
332
  private setupRouteChangeListener;
313
333
  private setupUnloadListener;
334
+ private captureUTMParams;
335
+ private loadPersistedAttribution;
336
+ private persistAttribution;
337
+ private detectChannel;
314
338
  }
315
339
 
316
340
  /**
package/dist/index.js CHANGED
@@ -2887,6 +2887,7 @@ var BATCH_TIMEOUT = 3e3;
2887
2887
  var MAX_STRING_LENGTH = 256;
2888
2888
  var STORAGE_KEY_QUEUE = "blinkAnalyticsQueue";
2889
2889
  var STORAGE_KEY_SESSION = "blinkAnalyticsSession";
2890
+ var STORAGE_KEY_ATTRIBUTION = "blinkAnalyticsAttribution";
2890
2891
  var BlinkAnalyticsImpl = class {
2891
2892
  httpClient;
2892
2893
  projectId;
@@ -2894,7 +2895,10 @@ var BlinkAnalyticsImpl = class {
2894
2895
  timer = null;
2895
2896
  enabled = true;
2896
2897
  userId = null;
2898
+ userEmail = null;
2897
2899
  hasTrackedPageview = false;
2900
+ utmParams = {};
2901
+ persistedAttribution = {};
2898
2902
  constructor(httpClient, projectId) {
2899
2903
  this.httpClient = httpClient;
2900
2904
  this.projectId = projectId;
@@ -2905,6 +2909,8 @@ var BlinkAnalyticsImpl = class {
2905
2909
  this.enabled = false;
2906
2910
  return;
2907
2911
  }
2912
+ this.loadPersistedAttribution();
2913
+ this.captureUTMParams();
2908
2914
  this.loadQueue();
2909
2915
  this.trackPageview();
2910
2916
  this.setupRouteChangeListener();
@@ -2945,18 +2951,42 @@ var BlinkAnalyticsImpl = class {
2945
2951
  setUserId(userId) {
2946
2952
  this.userId = userId;
2947
2953
  }
2954
+ /**
2955
+ * Set the user email for analytics events
2956
+ */
2957
+ setUserEmail(email) {
2958
+ this.userEmail = email;
2959
+ }
2960
+ /**
2961
+ * Clear persisted attribution data
2962
+ */
2963
+ clearAttribution() {
2964
+ this.persistedAttribution = {};
2965
+ try {
2966
+ localStorage.removeItem(STORAGE_KEY_ATTRIBUTION);
2967
+ } catch {
2968
+ }
2969
+ }
2948
2970
  // Private methods
2949
2971
  buildEvent(type, data = {}) {
2950
2972
  const sessionId = this.getOrCreateSessionId();
2973
+ const channel = this.detectChannel();
2951
2974
  return {
2952
2975
  type,
2953
2976
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2954
2977
  project_id: this.projectId,
2955
2978
  user_id: this.userId,
2979
+ user_email: this.userEmail,
2956
2980
  session_id: sessionId,
2957
2981
  pathname: window.location.pathname,
2958
2982
  referrer: document.referrer || null,
2959
2983
  screen_width: window.innerWidth,
2984
+ channel,
2985
+ utm_source: this.utmParams.utm_source || this.persistedAttribution.utm_source || null,
2986
+ utm_medium: this.utmParams.utm_medium || this.persistedAttribution.utm_medium || null,
2987
+ utm_campaign: this.utmParams.utm_campaign || this.persistedAttribution.utm_campaign || null,
2988
+ utm_content: this.utmParams.utm_content || this.persistedAttribution.utm_content || null,
2989
+ utm_term: this.utmParams.utm_term || this.persistedAttribution.utm_term || null,
2960
2990
  ...this.sanitizeData(data)
2961
2991
  };
2962
2992
  }
@@ -3091,6 +3121,73 @@ var BlinkAnalyticsImpl = class {
3091
3121
  this.flush();
3092
3122
  });
3093
3123
  }
3124
+ captureUTMParams() {
3125
+ const urlParams = new URLSearchParams(window.location.search);
3126
+ this.utmParams = {
3127
+ utm_source: urlParams.get("utm_source"),
3128
+ utm_medium: urlParams.get("utm_medium"),
3129
+ utm_campaign: urlParams.get("utm_campaign"),
3130
+ utm_content: urlParams.get("utm_content"),
3131
+ utm_term: urlParams.get("utm_term")
3132
+ };
3133
+ const hasNewParams = Object.values(this.utmParams).some((v) => v !== null);
3134
+ if (hasNewParams) {
3135
+ this.persistAttribution();
3136
+ }
3137
+ }
3138
+ loadPersistedAttribution() {
3139
+ try {
3140
+ const stored = localStorage.getItem(STORAGE_KEY_ATTRIBUTION);
3141
+ if (stored) {
3142
+ this.persistedAttribution = JSON.parse(stored);
3143
+ }
3144
+ } catch {
3145
+ this.persistedAttribution = {};
3146
+ }
3147
+ }
3148
+ persistAttribution() {
3149
+ try {
3150
+ const attribution = {
3151
+ ...this.persistedAttribution,
3152
+ ...Object.fromEntries(
3153
+ Object.entries(this.utmParams).filter(([_, v]) => v !== null)
3154
+ )
3155
+ };
3156
+ localStorage.setItem(STORAGE_KEY_ATTRIBUTION, JSON.stringify(attribution));
3157
+ this.persistedAttribution = attribution;
3158
+ } catch {
3159
+ }
3160
+ }
3161
+ detectChannel() {
3162
+ const referrer = document.referrer;
3163
+ const utmMedium = this.utmParams.utm_medium;
3164
+ this.utmParams.utm_source;
3165
+ if (utmMedium) {
3166
+ if (utmMedium === "cpc" || utmMedium === "ppc") return "Paid Search";
3167
+ if (utmMedium === "email") return "Email";
3168
+ if (utmMedium === "social") return "Social";
3169
+ if (utmMedium === "referral") return "Referral";
3170
+ if (utmMedium === "display") return "Display";
3171
+ if (utmMedium === "affiliate") return "Affiliate";
3172
+ }
3173
+ if (!referrer) return "Direct";
3174
+ try {
3175
+ const referrerUrl = new URL(referrer);
3176
+ const referrerDomain = referrerUrl.hostname.toLowerCase();
3177
+ if (/google\.|bing\.|yahoo\.|duckduckgo\.|baidu\.|yandex\./.test(referrerDomain)) {
3178
+ return "Organic Search";
3179
+ }
3180
+ if (/facebook\.|twitter\.|linkedin\.|instagram\.|youtube\.|tiktok\.|reddit\./.test(referrerDomain)) {
3181
+ return "Social";
3182
+ }
3183
+ if (/mail\.|outlook\.|gmail\./.test(referrerDomain)) {
3184
+ return "Email";
3185
+ }
3186
+ return "Referral";
3187
+ } catch {
3188
+ return "Direct";
3189
+ }
3190
+ }
3094
3191
  };
3095
3192
 
3096
3193
  // src/client.ts
@@ -3121,8 +3218,10 @@ var BlinkClientImpl = class {
3121
3218
  this.auth.onAuthStateChanged((state) => {
3122
3219
  if (state.isAuthenticated && state.user) {
3123
3220
  this.analytics.setUserId(state.user.id);
3221
+ this.analytics.setUserEmail(state.user.email);
3124
3222
  } else {
3125
3223
  this.analytics.setUserId(null);
3224
+ this.analytics.setUserEmail(null);
3126
3225
  }
3127
3226
  });
3128
3227
  }
package/dist/index.mjs CHANGED
@@ -2885,6 +2885,7 @@ var BATCH_TIMEOUT = 3e3;
2885
2885
  var MAX_STRING_LENGTH = 256;
2886
2886
  var STORAGE_KEY_QUEUE = "blinkAnalyticsQueue";
2887
2887
  var STORAGE_KEY_SESSION = "blinkAnalyticsSession";
2888
+ var STORAGE_KEY_ATTRIBUTION = "blinkAnalyticsAttribution";
2888
2889
  var BlinkAnalyticsImpl = class {
2889
2890
  httpClient;
2890
2891
  projectId;
@@ -2892,7 +2893,10 @@ var BlinkAnalyticsImpl = class {
2892
2893
  timer = null;
2893
2894
  enabled = true;
2894
2895
  userId = null;
2896
+ userEmail = null;
2895
2897
  hasTrackedPageview = false;
2898
+ utmParams = {};
2899
+ persistedAttribution = {};
2896
2900
  constructor(httpClient, projectId) {
2897
2901
  this.httpClient = httpClient;
2898
2902
  this.projectId = projectId;
@@ -2903,6 +2907,8 @@ var BlinkAnalyticsImpl = class {
2903
2907
  this.enabled = false;
2904
2908
  return;
2905
2909
  }
2910
+ this.loadPersistedAttribution();
2911
+ this.captureUTMParams();
2906
2912
  this.loadQueue();
2907
2913
  this.trackPageview();
2908
2914
  this.setupRouteChangeListener();
@@ -2943,18 +2949,42 @@ var BlinkAnalyticsImpl = class {
2943
2949
  setUserId(userId) {
2944
2950
  this.userId = userId;
2945
2951
  }
2952
+ /**
2953
+ * Set the user email for analytics events
2954
+ */
2955
+ setUserEmail(email) {
2956
+ this.userEmail = email;
2957
+ }
2958
+ /**
2959
+ * Clear persisted attribution data
2960
+ */
2961
+ clearAttribution() {
2962
+ this.persistedAttribution = {};
2963
+ try {
2964
+ localStorage.removeItem(STORAGE_KEY_ATTRIBUTION);
2965
+ } catch {
2966
+ }
2967
+ }
2946
2968
  // Private methods
2947
2969
  buildEvent(type, data = {}) {
2948
2970
  const sessionId = this.getOrCreateSessionId();
2971
+ const channel = this.detectChannel();
2949
2972
  return {
2950
2973
  type,
2951
2974
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2952
2975
  project_id: this.projectId,
2953
2976
  user_id: this.userId,
2977
+ user_email: this.userEmail,
2954
2978
  session_id: sessionId,
2955
2979
  pathname: window.location.pathname,
2956
2980
  referrer: document.referrer || null,
2957
2981
  screen_width: window.innerWidth,
2982
+ channel,
2983
+ utm_source: this.utmParams.utm_source || this.persistedAttribution.utm_source || null,
2984
+ utm_medium: this.utmParams.utm_medium || this.persistedAttribution.utm_medium || null,
2985
+ utm_campaign: this.utmParams.utm_campaign || this.persistedAttribution.utm_campaign || null,
2986
+ utm_content: this.utmParams.utm_content || this.persistedAttribution.utm_content || null,
2987
+ utm_term: this.utmParams.utm_term || this.persistedAttribution.utm_term || null,
2958
2988
  ...this.sanitizeData(data)
2959
2989
  };
2960
2990
  }
@@ -3089,6 +3119,73 @@ var BlinkAnalyticsImpl = class {
3089
3119
  this.flush();
3090
3120
  });
3091
3121
  }
3122
+ captureUTMParams() {
3123
+ const urlParams = new URLSearchParams(window.location.search);
3124
+ this.utmParams = {
3125
+ utm_source: urlParams.get("utm_source"),
3126
+ utm_medium: urlParams.get("utm_medium"),
3127
+ utm_campaign: urlParams.get("utm_campaign"),
3128
+ utm_content: urlParams.get("utm_content"),
3129
+ utm_term: urlParams.get("utm_term")
3130
+ };
3131
+ const hasNewParams = Object.values(this.utmParams).some((v) => v !== null);
3132
+ if (hasNewParams) {
3133
+ this.persistAttribution();
3134
+ }
3135
+ }
3136
+ loadPersistedAttribution() {
3137
+ try {
3138
+ const stored = localStorage.getItem(STORAGE_KEY_ATTRIBUTION);
3139
+ if (stored) {
3140
+ this.persistedAttribution = JSON.parse(stored);
3141
+ }
3142
+ } catch {
3143
+ this.persistedAttribution = {};
3144
+ }
3145
+ }
3146
+ persistAttribution() {
3147
+ try {
3148
+ const attribution = {
3149
+ ...this.persistedAttribution,
3150
+ ...Object.fromEntries(
3151
+ Object.entries(this.utmParams).filter(([_, v]) => v !== null)
3152
+ )
3153
+ };
3154
+ localStorage.setItem(STORAGE_KEY_ATTRIBUTION, JSON.stringify(attribution));
3155
+ this.persistedAttribution = attribution;
3156
+ } catch {
3157
+ }
3158
+ }
3159
+ detectChannel() {
3160
+ const referrer = document.referrer;
3161
+ const utmMedium = this.utmParams.utm_medium;
3162
+ this.utmParams.utm_source;
3163
+ if (utmMedium) {
3164
+ if (utmMedium === "cpc" || utmMedium === "ppc") return "Paid Search";
3165
+ if (utmMedium === "email") return "Email";
3166
+ if (utmMedium === "social") return "Social";
3167
+ if (utmMedium === "referral") return "Referral";
3168
+ if (utmMedium === "display") return "Display";
3169
+ if (utmMedium === "affiliate") return "Affiliate";
3170
+ }
3171
+ if (!referrer) return "Direct";
3172
+ try {
3173
+ const referrerUrl = new URL(referrer);
3174
+ const referrerDomain = referrerUrl.hostname.toLowerCase();
3175
+ if (/google\.|bing\.|yahoo\.|duckduckgo\.|baidu\.|yandex\./.test(referrerDomain)) {
3176
+ return "Organic Search";
3177
+ }
3178
+ if (/facebook\.|twitter\.|linkedin\.|instagram\.|youtube\.|tiktok\.|reddit\./.test(referrerDomain)) {
3179
+ return "Social";
3180
+ }
3181
+ if (/mail\.|outlook\.|gmail\./.test(referrerDomain)) {
3182
+ return "Email";
3183
+ }
3184
+ return "Referral";
3185
+ } catch {
3186
+ return "Direct";
3187
+ }
3188
+ }
3092
3189
  };
3093
3190
 
3094
3191
  // src/client.ts
@@ -3119,8 +3216,10 @@ var BlinkClientImpl = class {
3119
3216
  this.auth.onAuthStateChanged((state) => {
3120
3217
  if (state.isAuthenticated && state.user) {
3121
3218
  this.analytics.setUserId(state.user.id);
3219
+ this.analytics.setUserEmail(state.user.email);
3122
3220
  } else {
3123
3221
  this.analytics.setUserId(null);
3222
+ this.analytics.setUserEmail(null);
3124
3223
  }
3125
3224
  });
3126
3225
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blinkdotnew/sdk",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Blink TypeScript SDK for client-side applications - Zero-boilerplate CRUD + auth + AI + analytics + notifications for modern SaaS/AI apps",
5
5
  "keywords": [
6
6
  "blink",