# DataLayer Validation Reference

## DataLayer Structure Basics

### Proper Initialization
```javascript
// Must appear BEFORE GTM script
<script>
  window.dataLayer = window.dataLayer || [];
</script>
<!-- GTM script here -->
```

### Push Syntax
```javascript
// Correct
dataLayer.push({ event: "page_view", page_title: "Home" });

// Wrong - direct assignment
dataLayer = [{ event: "page_view" }];  // ❌ Overwrites array
```

## Validation Rules

### Event Names

| Rule | Valid | Invalid |
|------|-------|---------|
| Alphanumeric + underscore | `add_to_cart` | `add-to-cart` |
| Max 40 characters | `purchase` | (too long names) |
| Case sensitive | `addToCart` ≠ `addtocart` | - |
| No spaces | `form_submit` | `form submit` |
| No special chars | `click_cta` | `click@cta` |

### Parameter Names

| Rule | Valid | Invalid |
|------|-------|---------|
| Max 40 characters | `item_category` | (too long) |
| Alphanumeric + underscore | `user_id` | `user-id` |
| Cannot start with `_` | `custom_param` | `_private` |
| Cannot start with number | `step_1` | `1_step` |

### Data Types

| Parameter | Expected Type | Example |
|-----------|---------------|---------|
| value | number | `29.99` not `"29.99"` |
| currency | string (ISO 4217) | `"USD"`, `"KRW"` |
| transaction_id | string | `"T_12345"` |
| quantity | integer | `2` not `2.0` |
| price | number | `45000` |
| items | array | `[{...}, {...}]` |

### Type Validation Code

```javascript
function validateDataLayerPush(data) {
  const issues = [];
  
  // Check value is number
  if (data.ecommerce?.value !== undefined) {
    if (typeof data.ecommerce.value !== 'number') {
      issues.push(`value should be number, got ${typeof data.ecommerce.value}`);
    }
  }
  
  // Check currency format
  if (data.ecommerce?.currency) {
    if (!/^[A-Z]{3}$/.test(data.ecommerce.currency)) {
      issues.push(`currency should be 3-letter ISO code`);
    }
  }
  
  // Check items array
  if (data.ecommerce?.items) {
    if (!Array.isArray(data.ecommerce.items)) {
      issues.push(`items should be array`);
    } else {
      data.ecommerce.items.forEach((item, i) => {
        if (!item.item_id) issues.push(`items[${i}] missing item_id`);
        if (!item.item_name) issues.push(`items[${i}] missing item_name`);
        if (item.price && typeof item.price !== 'number') {
          issues.push(`items[${i}].price should be number`);
        }
        if (item.quantity && !Number.isInteger(item.quantity)) {
          issues.push(`items[${i}].quantity should be integer`);
        }
      });
    }
  }
  
  return issues;
}
```

## E-commerce Object Clearing

### Why Clear?
GA4 may merge previous ecommerce data with new events.

### Correct Pattern
```javascript
// Clear first
dataLayer.push({ ecommerce: null });

// Then push new event
dataLayer.push({
  event: "view_item",
  ecommerce: { ... }
});
```

### Validation Check
```javascript
function checkEcommerceClear(dataLayerArray) {
  let lastHadEcommerce = false;
  const issues = [];
  
  dataLayerArray.forEach((item, i) => {
    const hasEcommerce = 'ecommerce' in item;
    const isNull = item.ecommerce === null;
    
    if (hasEcommerce && !isNull && lastHadEcommerce) {
      issues.push({
        index: i,
        message: 'Missing ecommerce:null before this push'
      });
    }
    
    lastHadEcommerce = hasEcommerce && !isNull;
  });
  
  return issues;
}
```

## Event Sequence Validation

### Expected Sequences

**E-commerce Purchase Flow:**
```
view_item_list? → view_item → add_to_cart → view_cart → 
begin_checkout → add_shipping_info → add_payment_info → purchase
```

**Form Submission:**
```
form_start → form_submit → generate_lead?
```

**User Authentication:**
```
login | sign_up
```

### Sequence Validator

```javascript
function validateSequence(events, expectedOrder) {
  const eventNames = events
    .filter(e => e.event)
    .map(e => e.event);
  
  let lastIndex = -1;
  const issues = [];
  
  eventNames.forEach(event => {
    const index = expectedOrder.indexOf(event);
    if (index !== -1) {
      if (index < lastIndex) {
        issues.push(`${event} fired out of expected order`);
      }
      lastIndex = index;
    }
  });
  
  return issues;
}
```

## Duplicate Event Detection

### Common Duplicates
- Multiple `page_view` on single page load
- `purchase` firing on page refresh
- Click events on bubbling elements

### Detection Code

```javascript
function findDuplicates(events) {
  const seen = {};
  const duplicates = [];
  
  events.forEach((event, i) => {
    if (!event.event) return;
    
    const key = JSON.stringify(event);
    if (seen[key]) {
      duplicates.push({
        event: event.event,
        firstIndex: seen[key],
        duplicateIndex: i
      });
    } else {
      seen[key] = i;
    }
  });
  
  return duplicates;
}
```

## Real-time Monitoring Setup

### Console Monitoring

```javascript
// Paste in browser console to monitor pushes
(function() {
  const original = dataLayer.push;
  dataLayer.push = function() {
    console.group('📊 dataLayer.push');
    console.log('Data:', arguments[0]);
    console.log('Time:', new Date().toISOString());
    console.groupEnd();
    return original.apply(this, arguments);
  };
  console.log('✅ DataLayer monitoring active');
})();
```

### Export DataLayer

```javascript
// Copy full dataLayer to clipboard
copy(JSON.stringify(dataLayer, null, 2));
```

## Validation Checklist

### Structure
- [ ] dataLayer initialized before GTM
- [ ] Using push() not assignment
- [ ] Event names follow conventions
- [ ] Parameter names follow conventions

### Data Types
- [ ] value is number
- [ ] currency is 3-letter code
- [ ] quantity is integer
- [ ] items is array
- [ ] Required fields present

### E-commerce
- [ ] ecommerce:null before each push
- [ ] items array has item_id and item_name
- [ ] transaction_id is unique
- [ ] Consistent currency across events

### Sequence
- [ ] Events fire in logical order
- [ ] No duplicate events
- [ ] Purchase fires only once

## Debug Tools

### GTM Preview Mode
- Real-time event inspection
- Variable value checking
- Tag firing verification

### GA4 DebugView
- Live event stream
- Parameter validation
- User property tracking

### Browser Console
```javascript
// View current dataLayer
console.table(dataLayer);

// Filter by event
dataLayer.filter(d => d.event === 'purchase');
```
