@datalyr/web 1.0.7 → 1.1.1

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
@@ -21,6 +21,76 @@ The official Datalyr Web SDK for browser-based analytics. Track user behavior, m
21
21
  - 🔌 **Extensible** - Plugin system for custom functionality
22
22
  - 📦 **Container Scripts** - Securely manage third-party pixels and tracking scripts
23
23
 
24
+ ## Choosing the Right Installation Method
25
+
26
+ Datalyr offers two installation methods. Choose based on your needs:
27
+
28
+ ### Script Tag (Recommended for Most Cases)
29
+
30
+ **Use when:** Simple setup, zero bundle size, no build step needed
31
+
32
+ ```html
33
+ <script defer src="https://track.datalyr.com/container.js"
34
+ data-workspace-id="YOUR_WORKSPACE_ID"></script>
35
+ ```
36
+
37
+ **Pros:**
38
+ - ✅ Zero bundle size (external script)
39
+ - ✅ No npm install needed
40
+ - ✅ Works in any HTML page
41
+ - ✅ Automatic initialization
42
+ - ✅ Perfect for Next.js, React, Vue
43
+
44
+ **API Usage:**
45
+ ```javascript
46
+ // Available globally after script loads
47
+ window.datalyr.track('event_name', { key: 'value' })
48
+ window.datalyr.identify('user_123', { email: 'user@example.com' })
49
+ window.datalyr.getVisitorId()
50
+ ```
51
+
52
+ [See Script Tag Documentation →](https://docs.datalyr.com/installation/script-tag)
53
+
54
+ ---
55
+
56
+ ### NPM Package (For TypeScript/Bundlers)
57
+
58
+ **Use when:** You need TypeScript support, ES modules, or tree-shaking
59
+
60
+ ```bash
61
+ npm install @datalyr/web
62
+ ```
63
+
64
+ **Pros:**
65
+ - ✅ Full TypeScript definitions
66
+ - ✅ ES module imports
67
+ - ✅ Tree-shaking support
68
+ - ✅ Better IDE autocomplete
69
+
70
+ **API Usage:**
71
+ ```javascript
72
+ import datalyr from '@datalyr/web'
73
+
74
+ datalyr.init({ workspaceId: 'YOUR_WORKSPACE_ID' })
75
+ datalyr.track('event_name', { key: 'value' })
76
+ datalyr.identify('user_123', { email: 'user@example.com' })
77
+ ```
78
+
79
+ ---
80
+
81
+ ### ⚠️ Important: Don't Mix Both Methods
82
+
83
+ Choose **one** installation method. Using both can cause conflicts:
84
+
85
+ ```html
86
+ <!-- ❌ DON'T DO THIS -->
87
+ <script src="https://track.datalyr.com/container.js"></script>
88
+ <script>
89
+ import datalyr from '@datalyr/web' // Both methods at once!
90
+ datalyr.init({...})
91
+ </script>
92
+ ```
93
+
24
94
  ## Installation
25
95
 
26
96
  ```bash
@@ -65,19 +135,17 @@ import datalyr from '@datalyr/web';
65
135
  // Initialize the SDK with configuration
66
136
  datalyr.init({
67
137
  workspaceId: 'YOUR_WORKSPACE_ID',
68
-
138
+
69
139
  // Optional configuration
70
- // endpoint: 'https://custom.endpoint.com', // Optional: Custom endpoint
71
140
  debug: false, // Enable debug logging
72
- // batchSize: 10, // Optional: Events per batch
73
- // flushInterval: 5000, // Optional: Flush interval (ms)
74
141
  sessionTimeout: 1800000, // 30 minutes
75
142
  privacyMode: 'standard', // 'standard' or 'strict'
76
143
  respectDoNotTrack: false, // Honor browser DNT
77
144
  respectGlobalPrivacyControl: true, // Honor GPC
78
145
  cookieDomain: 'auto', // Auto-detect for subdomains
79
146
  trackSPA: true, // Track SPA route changes
80
- trackPageViews: true // Track initial page view
147
+ trackPageViews: true, // Track initial page view
148
+ enableContainer: true // Enable container script loading
81
149
  });
82
150
  ```
83
151
 
@@ -202,11 +270,11 @@ datalyr.setConsent({
202
270
  // Get current session ID
203
271
  const sessionId = datalyr.getSessionId();
204
272
 
205
- // Manually end session
206
- datalyr.endSession();
207
-
208
273
  // Start new session
209
274
  datalyr.startNewSession();
275
+
276
+ // Get session data
277
+ const sessionData = datalyr.getSessionData();
210
278
  ```
211
279
 
212
280
  ## Advanced Features
@@ -221,7 +289,7 @@ datalyr.track('Purchase', { value: 99.99 });
221
289
  datalyr.track('Signup', { plan: 'premium' });
222
290
  datalyr.track('Subscribe', { plan: 'annual' });
223
291
 
224
- // E-commerce events
292
+ // E-commerce events
225
293
  datalyr.track('Add to Cart', { product_id: 'SKU-123' });
226
294
  datalyr.track('Begin Checkout', { cart_value: 199.99 });
227
295
  ```
@@ -365,19 +433,19 @@ import datalyr from '@datalyr/web';
365
433
  // Create a plugin
366
434
  const myPlugin = {
367
435
  name: 'my-plugin',
368
-
436
+
369
437
  initialize(datalyr) {
370
438
  console.log('Plugin initialized');
371
439
  },
372
-
440
+
373
441
  track(eventName, properties) {
374
442
  console.log('Event tracked:', eventName);
375
443
  },
376
-
444
+
377
445
  identify(userId, traits) {
378
446
  console.log('User identified:', userId);
379
447
  },
380
-
448
+
381
449
  page(properties) {
382
450
  console.log('Page tracked');
383
451
  }
@@ -435,13 +503,13 @@ function App() {
435
503
  workspaceId: 'YOUR_WORKSPACE_ID'
436
504
  });
437
505
  }, []);
438
-
506
+
439
507
  const handleClick = () => {
440
508
  datalyr.track('Button Clicked', {
441
509
  button_name: 'CTA'
442
510
  });
443
511
  };
444
-
512
+
445
513
  return <button onClick={handleClick}>Click Me</button>;
446
514
  }
447
515
  ```
@@ -481,10 +549,19 @@ import datalyr from '@datalyr/web';
481
549
  export function AnalyticsProvider({ children }) {
482
550
  useEffect(() => {
483
551
  datalyr.init({
484
- workspaceId: process.env.NEXT_PUBLIC_DATALYR_WORKSPACE_ID
552
+ workspaceId: process.env.NEXT_PUBLIC_DATALYR_WORKSPACE_ID,
553
+ debug: process.env.NODE_ENV === 'development'
485
554
  });
555
+
556
+ // Verify initialization
557
+ console.log('✅ Datalyr initialized');
558
+ console.log('Session ID:', datalyr.getSessionId());
559
+ console.log('Anonymous ID:', datalyr.getAnonymousId());
560
+
561
+ // Track initial page view
562
+ datalyr.page();
486
563
  }, []);
487
-
564
+
488
565
  return children;
489
566
  }
490
567
 
@@ -516,13 +593,13 @@ When implementing authentication flows (login, signup, OAuth), you **MUST** call
516
593
  // ❌ WRONG - Server-side identify breaks attribution
517
594
  app.post('/api/login', async (req, res) => {
518
595
  const user = await authenticate(email, password);
519
-
596
+
520
597
  // This creates a NEW session without attribution data!
521
598
  await serverAnalytics.identify({
522
599
  userId: user.id,
523
600
  traits: { email: user.email }
524
601
  });
525
-
602
+
526
603
  res.json({ userId: user.id });
527
604
  });
528
605
  // Result: Lost UTM params, fbclid, gclid, referrer - all gone!
@@ -540,16 +617,16 @@ function LoginPage() {
540
617
  method: 'POST',
541
618
  body: JSON.stringify({ email, password })
542
619
  });
543
-
620
+
544
621
  const { userId, email, name } = await response.json();
545
-
622
+
546
623
  // 2. Identify in browser - this preserves attribution!
547
624
  datalyr.identify(userId, {
548
625
  email: email,
549
626
  name: name,
550
627
  login_method: 'email'
551
628
  });
552
-
629
+
553
630
  // 3. Then redirect
554
631
  router.push('/dashboard');
555
632
  };
@@ -558,7 +635,7 @@ function LoginPage() {
558
635
  // Server endpoint - NO identify, just auth
559
636
  app.post('/api/login', async (req, res) => {
560
637
  const user = await authenticate(email, password);
561
-
638
+
562
639
  // Just return user data for client-side identify
563
640
  res.json({
564
641
  userId: user.id,
@@ -605,7 +682,7 @@ useEffect(() => {
605
682
  auth_method: 'google',
606
683
  signup_source: localStorage.getItem('signup_source') || 'organic'
607
684
  });
608
-
685
+
609
686
  // Track the appropriate event
610
687
  if (user.isNewUser) {
611
688
  datalyr.track('Signup Completed', { method: 'oauth' });
@@ -619,9 +696,9 @@ useEffect(() => {
619
696
  // Server-side: OAuth callback - NO identify!
620
697
  export async function GET(request: Request) {
621
698
  const { user } = await validateOAuthCallback(request);
622
-
699
+
623
700
  const existingUser = await getUserByEmail(user.email);
624
-
701
+
625
702
  if (existingUser) {
626
703
  // Set session, but DON'T identify
627
704
  await createSession(existingUser.id);
@@ -644,10 +721,10 @@ Use server-side for **tracking events** with userId (not identify):
644
721
  // Stripe webhook example
645
722
  app.post('/webhook/stripe', async (req, res) => {
646
723
  const event = stripe.webhooks.constructEvent(req.body, sig, secret);
647
-
724
+
648
725
  if (event.type === 'checkout.session.completed') {
649
726
  const session = event.data.object;
650
-
727
+
651
728
  // Track purchase event with userId (NOT identify)
652
729
  await analytics.track({
653
730
  userId: session.client_reference_id, // Your user ID
@@ -733,9 +810,9 @@ async function handleLogin(email, password) {
733
810
  method: 'POST',
734
811
  body: JSON.stringify({ email, password })
735
812
  });
736
-
813
+
737
814
  const user = await response.json();
738
-
815
+
739
816
  // Identify in browser - preserves attribution!
740
817
  datalyr.identify(user.id, {
741
818
  email: user.email,
@@ -748,16 +825,14 @@ async function handleLogin(email, password) {
748
825
  // This breaks attribution tracking!
749
826
  app.post('/api/login', async (req, res) => {
750
827
  const user = await authenticate(req.body);
751
-
828
+
752
829
  // DON'T DO THIS - Creates new session
753
830
  await serverAnalytics.identify(user.id, {
754
831
  signup_date: new Date().toISOString()
755
832
  });
756
-
757
- datalyr.track('Signup Completed', {
758
- method: 'email'
759
- });
760
- }
833
+
834
+ res.json({ success: true });
835
+ });
761
836
  ```
762
837
 
763
838
  ### 5. Handle Errors Gracefully
@@ -775,31 +850,149 @@ window.addEventListener('error', (event) => {
775
850
  });
776
851
  ```
777
852
 
853
+ ## Troubleshooting
854
+
855
+ ### Events Not Showing in Dashboard?
856
+
857
+ **Step 1: Verify SDK is initialized**
858
+ ```javascript
859
+ import datalyr from '@datalyr/web'
860
+
861
+ // Check these return values
862
+ console.log('Anonymous ID:', datalyr.getAnonymousId()) // Should return "anon_xxxxx"
863
+ console.log('Session ID:', datalyr.getSessionId()) // Should return "sess_xxxxx"
864
+ ```
865
+
866
+ If these return `undefined`, the SDK isn't initialized properly.
867
+
868
+ **Step 2: Check for errors**
869
+ ```javascript
870
+ console.log('Errors:', datalyr.getErrors()) // Should be empty [] or show errors
871
+ ```
872
+
873
+ **Step 3: Enable debug mode**
874
+ ```javascript
875
+ datalyr.init({
876
+ workspaceId: 'YOUR_WORKSPACE_ID',
877
+ debug: true // Shows [Datalyr] logs in console
878
+ })
879
+ ```
880
+
881
+ **Step 4: Verify network requests**
882
+ 1. Open DevTools → Network tab
883
+ 2. Filter by "ingest"
884
+ 3. Track a test event: `datalyr.track('test', {})`
885
+ 4. You should see POST to `ingest.datalyr.com`
886
+
887
+ ### Common Errors
888
+
889
+ **"Cannot find module '@datalyr/web'"**
890
+ - Run `npm install @datalyr/web`
891
+ - Restart your dev server
892
+
893
+ **"process.env.NEXT_PUBLIC_DATALYR_WORKSPACE_ID is undefined"**
894
+ - Create `.env.local` file in project root
895
+ - Add: `NEXT_PUBLIC_DATALYR_WORKSPACE_ID=your_id`
896
+ - Restart dev server (required for env changes)
897
+
898
+ **"datalyr.getAnonymousId() returns undefined"**
899
+ - You're calling it during SSR (server-side rendering)
900
+ - Make sure you're in a Client Component with `'use client'`
901
+ - Call it inside `useEffect` or after component mounts
902
+
903
+ **Events tracked but not in dashboard**
904
+ - Verify workspace ID matches your dashboard
905
+ - Events may take 1-2 minutes to appear
906
+ - Check Network tab - requests should return 200 OK
907
+ - Check if ad blocker is enabled (disable it)
908
+
909
+ ### Script Tag vs SDK Confusion
910
+
911
+ **If you're using the script tag:**
912
+ ```javascript
913
+ // Check if loaded
914
+ window.datalyr._isLoaded // Should be true
915
+ window.datalyr.getVisitorId() // Should return ID
916
+
917
+ // Use object methods, not function calls
918
+ window.datalyr.track('event', {}) // ✅ Correct
919
+ window.datalyr("track", "event", {}) // ❌ Wrong
920
+ ```
921
+
922
+ **If you're using the NPM SDK:**
923
+ ```javascript
924
+ import datalyr from '@datalyr/web'
925
+
926
+ // Check if loaded
927
+ datalyr.getAnonymousId() // Should return ID
928
+ datalyr.getSessionId() // Should return ID
929
+ ```
930
+
778
931
  ## Migration from Script Tag
779
932
 
780
- If you're currently using the Datalyr script tag, migration is straightforward:
933
+ If you're currently using the Datalyr script tag and want to switch to the NPM package:
781
934
 
782
935
  ### Before (Script Tag):
783
936
  ```html
784
- <script>
785
- window.datalyr = window.datalyr || [];
786
- window.datalyr.push(['init', { workspace_id: 'YOUR_WORKSPACE_ID' }]);
787
- window.datalyr.push(['track', 'Button Clicked']);
788
- </script>
789
- <script async src="https://cdn.datalyr.io/datalyr.js"></script>
937
+ <!-- In your HTML -->
938
+ <script defer src="https://track.datalyr.com/container.js"
939
+ data-workspace-id="YOUR_WORKSPACE_ID"></script>
940
+ ```
941
+
942
+ ```javascript
943
+ // In your JavaScript
944
+ window.datalyr.track('Button Clicked', {
945
+ button_name: 'Sign Up'
946
+ });
947
+
948
+ window.datalyr.identify('user_123', {
949
+ email: 'user@example.com'
950
+ });
790
951
  ```
791
952
 
792
- ### After (Web SDK):
953
+ ### After (NPM SDK):
793
954
  ```javascript
955
+ // Remove the script tag from your HTML
956
+
957
+ // Install the package
958
+ // npm install @datalyr/web
959
+
960
+ // In your app initialization
794
961
  import datalyr from '@datalyr/web';
795
962
 
796
963
  datalyr.init({
797
964
  workspaceId: 'YOUR_WORKSPACE_ID'
798
965
  });
799
966
 
800
- datalyr.track('Button Clicked');
967
+ // In your components
968
+ datalyr.track('Button Clicked', {
969
+ button_name: 'Sign Up'
970
+ });
971
+
972
+ datalyr.identify('user_123', {
973
+ email: 'user@example.com'
974
+ });
801
975
  ```
802
976
 
977
+ **Note:** Most users should stay with the script tag unless they specifically need TypeScript support or ES module imports. The script tag has zero bundle size and works perfectly with Next.js, React, and Vue.
978
+
979
+ ## API Comparison: Script Tag vs NPM SDK
980
+
981
+ | Feature | Script Tag | NPM SDK |
982
+ |---------|-----------|---------|
983
+ | **Installation** | Add `<script>` to HTML | `npm install @datalyr/web` |
984
+ | **Bundle Size** | 0 (external) | ~15KB |
985
+ | **TypeScript** | No types | ✅ Full types |
986
+ | **Import** | `window.datalyr` | `import datalyr from '@datalyr/web'` |
987
+ | **Initialize** | Automatic | `datalyr.init({...})` |
988
+ | **Track Event** | `window.datalyr.track(...)` | `datalyr.track(...)` |
989
+ | **Identify** | `window.datalyr.identify(...)` | `datalyr.identify(...)` |
990
+ | **Get Visitor ID** | `window.datalyr.getVisitorId()` | `datalyr.getAnonymousId()` |
991
+ | **Get Session** | `window.datalyr._getSessionId()` | `datalyr.getSessionId()` |
992
+ | **Check Loaded** | `window.datalyr._isLoaded` | `datalyr.getAnonymousId() !== undefined` |
993
+
994
+ Both methods use the same tracking backend and provide identical attribution tracking.
995
+
803
996
  ## Browser Support
804
997
 
805
998
  The SDK supports all modern browsers:
@@ -823,7 +1016,7 @@ For older browsers, the SDK will gracefully degrade with fallback implementation
823
1016
  The SDK is designed with privacy in mind:
824
1017
 
825
1018
  - **GDPR Compliant**: Built-in consent management
826
- - **CCPA Support**: "Do Not Sell" preference support
1019
+ - **CCPA Support**: "Do Not Sell" preference support
827
1020
  - **Cookie Control**: Configurable cookie settings
828
1021
  - **Data Minimization**: Collect only what you need
829
1022
  - **User Rights**: Easy opt-out and data deletion
@@ -841,8 +1034,9 @@ datalyr.init({
841
1034
  });
842
1035
 
843
1036
  // Check console for detailed logs:
1037
+ // [Datalyr] SDK initialized
844
1038
  // [Datalyr] Event tracked: Button Clicked
845
- // [Datalyr] Events sent successfully
1039
+ // [Datalyr] Batch sent successfully: 5 events
846
1040
  ```
847
1041
 
848
1042
  ## API Reference
@@ -883,6 +1077,8 @@ datalyr.init({
883
1077
  | `getUserId()` | Get user ID |
884
1078
  | `getDistinctId()` | Get distinct ID |
885
1079
  | `getSessionId()` | Get session ID |
1080
+ | `getSessionData()` | Get session data |
1081
+ | `getErrors()` | Get SDK errors |
886
1082
  | `loadScript(scriptId)` | Manually trigger a container script |
887
1083
  | `getLoadedScripts()` | Get list of loaded container scripts |
888
1084
  | `destroy()` | Clean up resources |
@@ -899,4 +1095,4 @@ MIT License - see [LICENSE](LICENSE) file for details.
899
1095
 
900
1096
  ---
901
1097
 
902
- Built with ❤️ by the Datalyr team
1098
+ Built with ❤️ by the Datalyr team
@@ -6,6 +6,7 @@ import type { Attribution, TouchPoint } from './types';
6
6
  export declare class AttributionManager {
7
7
  private attributionWindow;
8
8
  private trackedParams;
9
+ private queryParamsCache;
9
10
  private UTM_PARAMS;
10
11
  private CLICK_IDS;
11
12
  private DEFAULT_TRACKED_PARAMS;
@@ -13,24 +14,34 @@ export declare class AttributionManager {
13
14
  attributionWindow?: number;
14
15
  trackedParams?: string[];
15
16
  });
17
+ /**
18
+ * Clear query params cache (called on page navigation)
19
+ * FIXED: Prevents stale attribution data on SPA navigation
20
+ */
21
+ clearCache(): void;
16
22
  /**
17
23
  * Capture current attribution from URL
18
24
  */
19
25
  captureAttribution(): Attribution;
20
26
  /**
21
- * Store first touch attribution
27
+ * Store first touch attribution with 90-day expiration
28
+ *
29
+ * FIXED (DATA-01): Removed paid priority logic that was corrupting first-touch attribution.
30
+ * First-touch is now IMMUTABLE except for expiration - this ensures accurate revenue attribution.
22
31
  */
23
32
  storeFirstTouch(attribution: Attribution): void;
24
33
  /**
25
34
  * Get first touch attribution
35
+ * Checks expiry and removes if expired (Issue #4)
26
36
  */
27
37
  getFirstTouch(): Attribution | null;
28
38
  /**
29
- * Store last touch attribution
39
+ * Store last touch attribution with 90-day expiration
30
40
  */
31
41
  storeLastTouch(attribution: Attribution): void;
32
42
  /**
33
43
  * Get last touch attribution
44
+ * Checks expiry and removes if expired (Issue #4)
34
45
  */
35
46
  getLastTouch(): Attribution | null;
36
47
  /**
@@ -47,10 +58,12 @@ export declare class AttributionManager {
47
58
  private captureAdCookies;
48
59
  /**
49
60
  * Check if we have a specific click ID in current params
61
+ * Uses cached params to avoid multiple URL parses (Issue #3)
50
62
  */
51
63
  private hasClickId;
52
64
  /**
53
65
  * Get current fbclid from URL if present
66
+ * Uses cached params to avoid multiple URL parses (Issue #3)
54
67
  */
55
68
  private getCurrentFbclid;
56
69
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../src/attribution.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEvD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,aAAa,CAAW;IAChC,OAAO,CAAC,UAAU,CAA2E;IAE7F,OAAO,CAAC,SAAS,CAgBf;IAEF,OAAO,CAAC,sBAAsB,CAO5B;gBAEU,OAAO,GAAE;QACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB;IAMN;;OAEG;IACH,kBAAkB,IAAI,WAAW;IAwDjC;;OAEG;IACH,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAU/C;;OAEG;IACH,aAAa,IAAI,WAAW,GAAG,IAAI;IAInC;;OAEG;IACH,cAAc,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAO9C;;OAEG;IACH,YAAY,IAAI,WAAW,GAAG,IAAI;IAIlC;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,IAAI;IAqBhE;;OAEG;IACH,UAAU,IAAI,UAAU,EAAE;IAI1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA8CxB;;OAEG;IACH,OAAO,CAAC,UAAU;IAKlB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAKxB;;OAEG;IACH,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAsDzC;;OAEG;IACH,OAAO,CAAC,eAAe;IA6CvB;;OAEG;IACH,OAAO,CAAC,eAAe;IA2BvB;;OAEG;IACH,OAAO,CAAC,eAAe;IAQvB;;OAEG;IACH,oBAAoB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAKvD;;OAEG;IACH,uBAAuB,IAAI,IAAI;CAYhC"}
1
+ {"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../src/attribution.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEvD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,aAAa,CAAW;IAChC,OAAO,CAAC,gBAAgB,CAAuC;IAC/D,OAAO,CAAC,UAAU,CAA2E;IAE7F,OAAO,CAAC,SAAS,CAgBf;IAEF,OAAO,CAAC,sBAAsB,CAO5B;gBAEU,OAAO,GAAE;QACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB;IAMN;;;OAGG;IACH,UAAU,IAAI,IAAI;IAIlB;;OAEG;IACH,kBAAkB,IAAI,WAAW;IA8DjC;;;;;OAKG;IACH,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAwB/C;;;OAGG;IACH,aAAa,IAAI,WAAW,GAAG,IAAI;IASnC;;OAEG;IACH,cAAc,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAQ9C;;;OAGG;IACH,YAAY,IAAI,WAAW,GAAG,IAAI;IASlC;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,IAAI;IAqBhE;;OAEG;IACH,UAAU,IAAI,UAAU,EAAE;IAI1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA8CxB;;;OAGG;IACH,OAAO,CAAC,UAAU;IAKlB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAKxB;;OAEG;IACH,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IA0EzC;;OAEG;IACH,OAAO,CAAC,eAAe;IA6CvB;;OAEG;IACH,OAAO,CAAC,eAAe;IA2BvB;;OAEG;IACH,OAAO,CAAC,eAAe;IAQvB;;OAEG;IACH,oBAAoB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAKvD;;OAEG;IACH,uBAAuB,IAAI,IAAI;CAYhC"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Auto-Identify Module
3
+ *
4
+ * Automatically captures user identity (email) from:
5
+ * 1. Fetch/XHR API requests (email in request/response)
6
+ * 2. Form submissions (email inputs)
7
+ * 3. Shopify-specific endpoints (/account.json)
8
+ *
9
+ * SECURITY:
10
+ * - Email validation to prevent false positives
11
+ * - Whitelist approach for trusted domains
12
+ * - Rate limiting to prevent spam
13
+ * - Privacy controls integration
14
+ */
15
+ interface AutoIdentifyConfig {
16
+ enabled?: boolean;
17
+ captureFromForms?: boolean;
18
+ captureFromAPI?: boolean;
19
+ captureFromShopify?: boolean;
20
+ trustedDomains?: string[];
21
+ debug?: boolean;
22
+ }
23
+ export declare class AutoIdentifyManager {
24
+ private config;
25
+ private identifyCallback?;
26
+ private originalFetch?;
27
+ private originalXHROpen?;
28
+ private originalXHRSend?;
29
+ private formListeners;
30
+ private lastIdentifyTime;
31
+ private RATE_LIMIT_MS;
32
+ private shopifyCheckInterval?;
33
+ constructor(config?: AutoIdentifyConfig);
34
+ /**
35
+ * Initialize auto-identify system
36
+ */
37
+ initialize(identifyCallback: (email: string, source: string) => void): void;
38
+ /**
39
+ * Setup form monitoring for email capture
40
+ */
41
+ private setupFormMonitoring;
42
+ /**
43
+ * Scan DOM for forms with email inputs
44
+ */
45
+ private scanForEmailForms;
46
+ /**
47
+ * Handle form submission
48
+ */
49
+ private handleFormSubmit;
50
+ /**
51
+ * Setup fetch interception for API email capture
52
+ */
53
+ private setupFetchInterception;
54
+ /**
55
+ * Setup XHR interception for API email capture
56
+ */
57
+ private setupXHRInterception;
58
+ /**
59
+ * Setup Shopify-specific monitoring
60
+ */
61
+ private setupShopifyMonitoring;
62
+ /**
63
+ * Check if running on Shopify
64
+ */
65
+ private isShopify;
66
+ /**
67
+ * Check Shopify customer endpoint for email
68
+ */
69
+ private checkShopifyCustomer;
70
+ /**
71
+ * Extract email from data (object, string, FormData)
72
+ */
73
+ private extractEmailFromData;
74
+ /**
75
+ * Recursively find emails in object
76
+ */
77
+ private findEmailsInObject;
78
+ /**
79
+ * Check if domain is trusted for auto-identify
80
+ */
81
+ private isTrustedDomain;
82
+ /**
83
+ * Validate email format
84
+ */
85
+ private isValidEmail;
86
+ /**
87
+ * Trigger identify with rate limiting
88
+ */
89
+ private triggerIdentify;
90
+ /**
91
+ * Destroy and cleanup
92
+ */
93
+ destroy(): void;
94
+ /**
95
+ * Debug logging
96
+ */
97
+ private log;
98
+ }
99
+ export {};
100
+ //# sourceMappingURL=auto-identify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-identify.d.ts","sourceRoot":"","sources":["../src/auto-identify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,UAAU,kBAAkB;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,gBAAgB,CAAC,CAA0C;IACnE,OAAO,CAAC,aAAa,CAAC,CAAsB;IAC5C,OAAO,CAAC,eAAe,CAAC,CAAuC;IAC/D,OAAO,CAAC,eAAe,CAAC,CAAuC;IAC/D,OAAO,CAAC,aAAa,CAA+D;IACpF,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,oBAAoB,CAAC,CAAiC;gBAElD,MAAM,GAAE,kBAAuB;IAW3C;;OAEG;IACH,UAAU,CAAC,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAgC3E;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAiB3B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAuBzB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAoBxB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAgD9B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA2C5B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAmB9B;;OAEG;IACH,OAAO,CAAC,SAAS;IAQjB;;OAEG;YACW,oBAAoB;IA0BlC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAwC5B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAuB1B;;OAEG;IACH,OAAO,CAAC,eAAe;IAuBvB;;OAEG;IACH,OAAO,CAAC,YAAY;IAwBpB;;OAEG;IACH,OAAO,CAAC,eAAe;IA0BvB;;OAEG;IACH,OAAO,IAAI,IAAI;IAiCf;;OAEG;IACH,OAAO,CAAC,GAAG;CAKZ"}