Architecture

How the Service Worker + Loader architecture works, including data flow, fallback mechanism, and framework-specific integration guides.

🏗️ How It Works

Service Worker (SW)

  • Intercepts every navigate request
  • Extracts UTMs and click IDs from the URL
  • Detects traffic source via 60+ platform rules
  • Stores data in IndexedDB (30min TTL)
  • Injects digitalData into the HTML response

Loader (Client)

  • Registers the SW on first load
  • Receives digitalData from injected script
  • Exposes TrafficCampaign API
  • Fires trafficcampaign:ready event
  • Fallback: captures params without SW

Data Flow

  User clicks ad (utm_source=google&gclid=abc123)
    │
    ▼
  ┌─────────────────────────────────────────────────┐
  │  SERVICE WORKER (intercepts fetch → navigate)   │
  │                                                 │
  │  1. Extract URL params → sanitize (xss lib)     │
  │  2. Detect traffic: source, medium, channel     │
  │  3. Detect campaign provider (Google Ads, etc)  │
  │  4. Store in IndexedDB (30min TTL)              │
  │  5. Inject <script> with digitalData into HTML  │
  └─────────────────────────────────────────────────┘
    │
    ▼
  ┌─────────────────────────────────────────────────┐
  │  LOADER (runs in main thread)                   │
  │                                                 │
  │  1. Read injected digitalData                   │
  │  2. Expose window.__PRESERVED_DIGITALDATA__     │
  │  3. Expose window.TrafficCampaign API           │
  │  4. Fire 'trafficcampaign:ready' event          │
  └─────────────────────────────────────────────────┘
    │
    ▼
  ┌─────────────────────────────────────────────────┐
  │  YOUR APP                                       │
  │                                                 │
  │  TrafficCampaign.getDigitalData()               │
  │  TrafficCampaign.sendToGA('G-XXXXXXX')          │
  └─────────────────────────────────────────────────┘

Fallback Mechanism

If the Service Worker is unavailable (first load, unsupported browser, private browsing), the Loader captures URL parameters directly from location.search and provides a basic digitalData object. The data source is indicated in window.__DIGITALDATA_SOURCE__ ("service-worker" or "fallback").

Service Worker Scope: The SW must be served from your domain root (/service-worker.js) to intercept all navigation requests. If placed in a subdirectory, it will only capture pages under that path.

🔧 Framework Guides

Next.js (App Router)

# Step 1: Copy SW
cp node_modules/@impressio/sdk/dist/service-worker.js public/service-worker.js

// Step 2: Import in layout.js
import '@impressio/sdk';

Vite / React

// vite.config.js — Auto-copy SW on build
import { defineConfig } from 'vite';
import { copyFileSync } from 'fs';

export default defineConfig({
  plugins: [{
    name: 'copy-impressio-sw',
    buildStart() {
      copyFileSync(
        'node_modules/@impressio/sdk/dist/service-worker.js',
        'public/service-worker.js'
      );
    }
  }]
});

Google Tag Manager

// Push digitalData to GTM dataLayer
window.addEventListener('trafficcampaign:ready', (e) => {
  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push({
    event: 'impressio_campaign_detected',
    impressio_source: e.detail.trafficDetail.source,
    impressio_medium: e.detail.trafficDetail.medium,
    impressio_channel: e.detail.trafficDetail.channel
  });
});
SPA Note: In Single Page Applications, the SW only intercepts initial navigations. For soft navigations (client-side routing), use TrafficCampaign.refresh() to re-query the SW, or rely on the initially captured data.