# 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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://engine.sygnal.com/migrating-from-sse-to-sse2.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
