# Migrating from SSE to SSE2

## Overview

SSE v2.0.0 (sse-core) introduces significant improvements that make working with Webflow sites easier and more maintainable. This guide will walk you through migrating your existing SSE v1.x code to v2.0.0.

## Key Changes

1. **Package renamed**: `@sygnal/sse` → `@sygnal/sse-core`
2. **New base classes**: `PageBase` and `ComponentBase` with automatic context detection
3. **Lifecycle methods**: `setup()`/`exec()` → `onPrepare()`/`onLoad()`
4. **Singleton pattern**: Access page from components via `PageBase.getCurrentPage()`
5. **RouteDispatcher fix**: Must create once and reuse the instance

## Migration Steps

### Step 1: Update Package

Update your `package.json`:

```json
{
  "dependencies": {
    "@sygnal/sse-core": "^2.0.0"
  }
}
```

Then run:

```bash
npm install
```

### Step 2: Update Import Statements

Find and replace all imports throughout your project:

**Before:**

```typescript
import { IModule, page, component } from '@sygnal/sse';
```

**After:**

```typescript
import { PageBase, ComponentBase, page, component } from '@sygnal/sse-core';
```

### Step 3: Migrate Pages to PageBase

**Before (v1.x):**

```typescript
import { IModule, page } from '@sygnal/sse';

@page('/')
export class HomePage implements IModule {

  constructor() {}

  setup(): void {
    // Synchronous setup
    console.log('Setting up home page');
  }

  async exec(): Promise<void> {
    // Manual context detection
    const pageId = document.documentElement.getAttribute('data-wf-page');
    const collectionId = document.documentElement.getAttribute('data-wf-collection');

    console.log('Page ID:', pageId);
    console.log('Collection:', collectionId);
  }
}
```

**After (v2.0):**

```typescript
import { PageBase, page } from '@sygnal/sse-core';

@page('/')
export class HomePage extends PageBase {

  protected onPrepare(): void {
    // Synchronous setup - automatic context available!
    console.log('Setting up home page');
    console.log('Page ID:', this.pageInfo.pageId);
  }

  protected async onLoad(): Promise<void> {
    // Async execution - full context available
    console.log('Collection:', this.pageInfo.collectionId);
    console.log('Item Slug:', this.pageInfo.itemSlug);
    console.log('Query Params:', this.pageInfo.queryParams);
  }
}
```

**Key Changes:**

* `implements IModule` → `extends PageBase`
* Remove constructor (not needed)
* `setup()` → `protected onPrepare()`
* `exec()` → `protected async onLoad()`
* Access `this.pageInfo` for all Webflow context (no manual DOM queries!)

### Step 4: Migrate Components to ComponentBase

**Before (v1.x):**

```typescript
import { IModule, component } from '@sygnal/sse';

@component('navigation')
export class NavigationComponent implements IModule {
  private elem: HTMLElement;

  constructor(elem: HTMLElement) {
    this.elem = elem;
  }

  setup(): void {
    console.log('Navigation setup');
  }

  async exec(): Promise<void> {
    // Manual element access
    const componentName = this.elem.getAttribute('data-component');
    const customData = this.elem.getAttribute('data-nav-type');

    this.elem.addEventListener('click', () => {
      console.log('Navigation clicked');
    });
  }
}
```

**After (v2.0):**

```typescript
import { ComponentBase, component, PageBase } from '@sygnal/sse-core';

@component('navigation')
export class NavigationComponent extends ComponentBase {

  protected onPrepare(): void {
    // Automatic element and context available!
    console.log('Navigation setup');
    console.log('Component:', this.context.name);
  }

  protected async onLoad(): Promise<void> {
    // Access element directly
    const customData = this.element.getAttribute('data-nav-type');

    // Access current page if needed
    const page = PageBase.getCurrentPage();
    if (page) {
      console.log('Current page:', page.pageInfo.pageId);
    }

    this.element.addEventListener('click', () => {
      console.log('Navigation clicked');
    });
  }
}
```

**Key Changes:**

* `implements IModule` → `extends ComponentBase`
* Remove constructor (automatic element injection!)
* `setup()` → `protected onPrepare()`
* `exec()` → `protected async onLoad()`
* `this.elem` → `this.element` (automatic)
* Access `this.context` for component metadata
* Access page via `PageBase.getCurrentPage()` singleton

### Step 5: Fix RouteDispatcher Instance (CRITICAL)

This is a critical fix that prevents data loss between preparation and execution phases.

**Before (v1.x - BROKEN):**

```typescript
import { routeDispatcher } from './routes';

// This creates TWO different instances!
routeDispatcher().setupRoute();  // Instance A stores data
routeDispatcher().execRoute();   // Instance B has no data - LOST!
```

**After (v2.0 - CORRECT):**

```typescript
import { routeDispatcher } from './routes';

// Create ONCE and reuse the same instance
const dispatcher = routeDispatcher();
dispatcher.setupRoute();   // Instance stores data
dispatcher.execRoute();    // SAME instance has the data!
```

Update your `src/index.ts`:

```typescript
import { routeDispatcher } from './routes';

// Create dispatcher once
const dispatcher = routeDispatcher();

// Setup phase (runs in <head>)
dispatcher.setupRoute();

// Execution phase (runs after DOM ready)
window.Webflow ||= [];
window.Webflow.push(() => {
  dispatcher.execRoute();
});
```

### Step 6: Update Route Registration (Optional)

If using automatic route discovery with `getAllPages()`:

```typescript
import { RouteDispatcher, getAllPages } from "@sygnal/sse-core";
import { Site } from "./site";

// Import pages to trigger @page decorators
import "./pages/home";
import "./pages/blog";
import "./pages/about";

export const routeDispatcher = (): RouteDispatcher => {
    const dispatcher = new RouteDispatcher(Site);
    dispatcher.routes = getAllPages();  // Automatically populated!
    return dispatcher;
}
```

### Step 7: Keep Site Class Using IModule

The Site class doesn't need automatic context, so it still uses `IModule`:

```typescript
import { IModule } from "@sygnal/sse-core";

export class Site implements IModule {

  constructor() {}

  setup() {
    // Site-wide setup
  }

  exec() {
    // Site-wide execution
  }
}
```

## Available Automatic Context

### PageBase - this.pageInfo Properties

When extending `PageBase`, you automatically get access to:

* `this.pageInfo.path` - Current page path (e.g., "/blog/my-post")
* `this.pageInfo.url` - Full URL
* `this.pageInfo.hash` - URL hash fragment
* `this.pageInfo.queryParams` - Parsed query parameters object
* `this.pageInfo.pageId` - Webflow page ID
* `this.pageInfo.siteId` - Webflow site ID
* `this.pageInfo.collectionId` - CMS collection ID (if applicable)
* `this.pageInfo.itemId` - CMS item ID (if applicable)
* `this.pageInfo.itemSlug` - CMS item slug (if applicable)
* `this.pageInfo.domain` - Webflow domain
* `this.pageInfo.lang` - Page language

### ComponentBase - this Properties

When extending `ComponentBase`, you automatically get:

* `this.element` - The HTMLElement the component is bound to
* `this.context.name` - Component name from `data-component`
* `this.context.id` - Component ID from `data-component-id`
* `this.context.dataAttributes` - All data-\* attributes as key-value pairs

## Benefits Summary

| Feature                        | v1.x (IModule)            | v2.0 (Base Classes)         |
| ------------------------------ | ------------------------- | --------------------------- |
| **Context Detection**          | Manual DOM queries        | Automatic                   |
| **Page Info**                  | Parse attributes yourself | `this.pageInfo.*`           |
| **Component Element**          | Pass in constructor       | `this.element`              |
| **Lifecycle Naming**           | `setup()`/`exec()`        | `onPrepare()`/`onLoad()`    |
| **Page Access from Component** | Pass references manually  | `PageBase.getCurrentPage()` |
| **Type Safety**                | Manual typing             | Fully typed                 |
| **Boilerplate Code**           | Lots                      | Minimal                     |

## Testing Your Migration

After migrating, verify:

1. ✅ All pages load without console errors
2. ✅ Page-specific code executes on correct routes
3. ✅ Components initialize and function correctly
4. ✅ `this.pageInfo` contains expected values in pages
5. ✅ `this.element` and `this.context` work in components
6. ✅ `PageBase.getCurrentPage()` returns page instance from components
7. ✅ Data persists between `onPrepare()` and `onLoad()` phases

## Common Issues

### Issue: "Property 'pageInfo' does not exist"

**Cause:** Still using `implements IModule` instead of `extends PageBase`

**Fix:** Change class declaration:

```typescript
// Before
export class HomePage implements IModule {

// After
export class HomePage extends PageBase {
```

### Issue: Data lost between onPrepare() and onLoad()

**Cause:** Creating multiple RouteDispatcher instances

**Fix:** Store dispatcher in a variable and reuse:

```typescript
const dispatcher = routeDispatcher();
dispatcher.setupRoute();
dispatcher.execRoute();
```

### Issue: "Cannot read property 'pageId' of undefined"

**Cause:** Trying to access pageInfo before context detection completes

**Fix:** PageInfo is available in both `onPrepare()` and `onLoad()`, but ensure you're extending `PageBase` correctly.

## Backward Compatibility

The `IModule` interface is still fully supported for advanced use cases where you need manual control. You can mix and match:

* **Site class**: Continue using `IModule` (recommended)
* **Pages**: Use `PageBase` for automatic context (recommended)
* **Components**: Use `ComponentBase` for automatic context (recommended)
* **Advanced cases**: Use `IModule` when you need full manual control

## Need Help?

If you encounter issues during migration:

1. Check that all imports use `@sygnal/sse-core`
2. Verify RouteDispatcher is created once and reused
3. Ensure pages extend `PageBase` and components extend `ComponentBase`
4. Check that lifecycle methods use new names (`onPrepare`/`onLoad`)
5. Review the [sse-template](https://github.com/sygnaltech/sse-template) repository for complete examples
