@datalyr/web 1.0.4 → 1.0.6

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
@@ -504,6 +504,172 @@ export default function RootLayout({ children }) {
504
504
  }
505
505
  ```
506
506
 
507
+ ## Authentication & Attribution Tracking
508
+
509
+ ### ⚠️ Critical: Client-Side Identification for Attribution
510
+
511
+ When implementing authentication flows (login, signup, OAuth), you **MUST** call identify on the client-side (browser) to preserve attribution data. Server-side identification creates new sessions and loses all attribution context.
512
+
513
+ #### The Problem with Server-Side Identify
514
+
515
+ ```javascript
516
+ // ❌ WRONG - Server-side identify breaks attribution
517
+ app.post('/api/login', async (req, res) => {
518
+ const user = await authenticate(email, password);
519
+
520
+ // This creates a NEW session without attribution data!
521
+ await serverAnalytics.identify({
522
+ userId: user.id,
523
+ traits: { email: user.email }
524
+ });
525
+
526
+ res.json({ userId: user.id });
527
+ });
528
+ // Result: Lost UTM params, fbclid, gclid, referrer - all gone!
529
+ ```
530
+
531
+ #### The Correct Solution
532
+
533
+ ```javascript
534
+ // ✅ CORRECT - Client-side identification preserves attribution
535
+ // React/Next.js example
536
+ function LoginPage() {
537
+ const handleLogin = async () => {
538
+ // 1. Authenticate on server
539
+ const response = await fetch('/api/login', {
540
+ method: 'POST',
541
+ body: JSON.stringify({ email, password })
542
+ });
543
+
544
+ const { userId, email, name } = await response.json();
545
+
546
+ // 2. Identify in browser - this preserves attribution!
547
+ datalyr.identify(userId, {
548
+ email: email,
549
+ name: name,
550
+ login_method: 'email'
551
+ });
552
+
553
+ // 3. Then redirect
554
+ router.push('/dashboard');
555
+ };
556
+ }
557
+
558
+ // Server endpoint - NO identify, just auth
559
+ app.post('/api/login', async (req, res) => {
560
+ const user = await authenticate(email, password);
561
+
562
+ // Just return user data for client-side identify
563
+ res.json({
564
+ userId: user.id,
565
+ email: user.email,
566
+ name: user.name
567
+ });
568
+ });
569
+ ```
570
+
571
+ ### Why Client-Side Identification Matters
572
+
573
+ 1. **Attribution Preserved**: UTM params, fbclid, gclid, referrer all stay linked to the user
574
+ 2. **Session Continuity**: Links anonymous session (with all its context) to identified user
575
+ 3. **Cookie Access**: Can read and maintain browser session data
576
+ 4. **Accurate Journey**: Tracks the complete anonymous → identified user journey
577
+
578
+ ### Implementation Checklist
579
+
580
+ Ensure you call identify **client-side** in these scenarios:
581
+
582
+ - [ ] **After Signup Success**: When account creation API returns success
583
+ - [ ] **After Login Success**: When authentication API returns success
584
+ - [ ] **After OAuth Redirect**: When returning from OAuth provider
585
+ - [ ] **After Magic Link Click**: When magic link is validated
586
+ - [ ] **On App Load**: If user session exists, re-identify
587
+ - [ ] **After Email Verification**: When user verifies their email
588
+ - [ ] **Session Restore**: When detecting existing authenticated session
589
+
590
+ ### Example: Complete OAuth Implementation
591
+
592
+ ```javascript
593
+ // Client-side: After OAuth redirect
594
+ useEffect(() => {
595
+ // Check if we just came back from OAuth
596
+ if (window.location.search.includes('oauth=success')) {
597
+ // Get user data from your API/session
598
+ fetch('/api/me')
599
+ .then(res => res.json())
600
+ .then(user => {
601
+ // Identify in browser - preserves attribution!
602
+ datalyr.identify(user.id, {
603
+ email: user.email,
604
+ name: user.name,
605
+ auth_method: 'google',
606
+ signup_source: localStorage.getItem('signup_source') || 'organic'
607
+ });
608
+
609
+ // Track the appropriate event
610
+ if (user.isNewUser) {
611
+ datalyr.track('Signup Completed', { method: 'oauth' });
612
+ } else {
613
+ datalyr.track('Login Successful', { method: 'oauth' });
614
+ }
615
+ });
616
+ }
617
+ }, []);
618
+
619
+ // Server-side: OAuth callback - NO identify!
620
+ export async function GET(request: Request) {
621
+ const { user } = await validateOAuthCallback(request);
622
+
623
+ const existingUser = await getUserByEmail(user.email);
624
+
625
+ if (existingUser) {
626
+ // Set session, but DON'T identify
627
+ await createSession(existingUser.id);
628
+ return redirect('/dashboard?oauth=success');
629
+ } else {
630
+ // Create user, but DON'T identify
631
+ const newUser = await createUser(user);
632
+ await createSession(newUser.id);
633
+ return redirect('/dashboard?oauth=success&new=true');
634
+ }
635
+ }
636
+ ```
637
+
638
+ ### Server-Side Events (Different from Identify!)
639
+
640
+ Use server-side for **tracking events** with userId (not identify):
641
+
642
+ ```javascript
643
+ // ✅ CORRECT - Server-side events with userId
644
+ // Stripe webhook example
645
+ app.post('/webhook/stripe', async (req, res) => {
646
+ const event = stripe.webhooks.constructEvent(req.body, sig, secret);
647
+
648
+ if (event.type === 'checkout.session.completed') {
649
+ const session = event.data.object;
650
+
651
+ // Track purchase event with userId (NOT identify)
652
+ await analytics.track({
653
+ userId: session.client_reference_id, // Your user ID
654
+ event: 'Purchase Completed',
655
+ properties: {
656
+ amount: session.amount_total / 100,
657
+ currency: session.currency,
658
+ plan: session.metadata.plan
659
+ }
660
+ });
661
+ }
662
+ });
663
+ ```
664
+
665
+ ### What Happens with Server-Side Identify (Wrong!)
666
+
667
+ If you identify server-side:
668
+ - **Lost Attribution**: New session created, no UTM params or click IDs
669
+ - **Broken Journey**: Can't link anonymous browsing to identified user
670
+ - **Multiple Sessions**: Each server identify creates disconnected session
671
+ - **No Context**: Missing referrer, landing page, device info
672
+
507
673
  ## Best Practices
508
674
 
509
675
  ### 1. Initialize Early
@@ -557,15 +723,20 @@ datalyr.track('Product Viewed', {
557
723
  });
558
724
  ```
559
725
 
560
- ### 4. Identify Users After Authentication
561
- Call identify as soon as you know who the user is:
726
+ ### 4. ALWAYS Identify in the Browser (Not Server)
727
+ This is critical for attribution:
562
728
 
563
729
  ```javascript
564
- // After login
730
+ // CORRECT - Browser identification
565
731
  async function handleLogin(email, password) {
566
- const user = await loginUser(email, password);
732
+ const response = await fetch('/api/login', {
733
+ method: 'POST',
734
+ body: JSON.stringify({ email, password })
735
+ });
736
+
737
+ const user = await response.json();
567
738
 
568
- // Identify immediately after login
739
+ // Identify in browser - preserves attribution!
569
740
  datalyr.identify(user.id, {
570
741
  email: user.email,
571
742
  name: user.name,
@@ -573,13 +744,13 @@ async function handleLogin(email, password) {
573
744
  });
574
745
  }
575
746
 
576
- // After signup
577
- async function handleSignup(data) {
578
- const user = await createUser(data);
747
+ // WRONG - Server identification
748
+ // This breaks attribution tracking!
749
+ app.post('/api/login', async (req, res) => {
750
+ const user = await authenticate(req.body);
579
751
 
580
- datalyr.identify(user.id, {
581
- email: data.email,
582
- name: data.name,
752
+ // DON'T DO THIS - Creates new session
753
+ await serverAnalytics.identify(user.id, {
583
754
  signup_date: new Date().toISOString()
584
755
  });
585
756
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @datalyr/web v1.0.4
2
+ * @datalyr/web v1.0.6
3
3
  * Datalyr Web SDK - Modern attribution tracking for web applications
4
4
  * (c) 2025 Datalyr Inc.
5
5
  * Released under the MIT License
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @datalyr/web v1.0.4
2
+ * @datalyr/web v1.0.6
3
3
  * Datalyr Web SDK - Modern attribution tracking for web applications
4
4
  * (c) 2025 Datalyr Inc.
5
5
  * Released under the MIT License
package/dist/datalyr.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @datalyr/web v1.0.4
2
+ * @datalyr/web v1.0.6
3
3
  * Datalyr Web SDK - Modern attribution tracking for web applications
4
4
  * (c) 2025 Datalyr Inc.
5
5
  * Released under the MIT License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalyr/web",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Datalyr Web SDK - Modern attribution tracking for web applications",
5
5
  "main": "dist/datalyr.js",
6
6
  "module": "dist/datalyr.esm.js",