# Coralite Framework

## CRITICAL DIRECTIVES
When generating code or answering questions about Coralite, you MUST adhere to the following rules:
1. **NO VANILLA BOILERPLATE:** Do not write standard `customElements.define()` or `class extends HTMLElement` blocks for components. You MUST always use Coralite's `defineComponent` exported from `coralite`.
2. **AST SPLICING AWARENESS:** If a component is written declaratively in HTML (e.g., `<my-component>`), the server completely deletes the host tag and replaces it with the template's inner HTML.
3. **ZERO-CONFIG DEPENDENCIES:** Coralite uses AST static analysis. To import JavaScript on the client, use standard dynamic imports (`await import('pkg')`) inside the `script` block. To mount a Web Component imperatively, use `document.createElement('my-component')` using a string literal containing a hyphen, which the bundler automatically detects.

---

## 1. The Serialization Boundary
**CRITICAL RULE:** You cannot use top-level imports or variables (declared outside `defineComponent`) directly inside the `script` block. 
The `script` block is serialized and sent to the browser. To bridge server-side data to the client, you must use the `data` block.

## 2. File Structure: Pages vs. Components
* **Pages**: Pages are standard `.html` files (e.g., `index.html`) that serve as route entry points. They act as the **consumers** of components by declaring custom element tags within their HTML body (e.g., `<my-navbar></my-navbar>`).
* **Components**: A Coralite component is strictly an `.html` file. It is not a `.js` or `.jsx` file. A component file consists of up to three top-level root tags:
  1. `<template id="component-name">` (**Required**): Defines the declarative HTML structure.
  2. `<style>` (**Optional**): Defines component-specific styles.
  3. `<script type="module">` (**Optional**): Defines the reactive logic using `defineComponent`.
* **The Relationship**: When a Page consumes a component tag, the server-side build process discovers the component, completely deletes the host tag from the page, and splices the component's inner `<template>` HTML, `<style>`, and serialized `<script>` into the page output.

## 3. System Architecture
* **Rendering**: Strictly Server-Side Rendered (SSR). No client-side hydration.
* **Architecture**: Coralite utilizes a hybrid model where components are either fully declarative (SSR-only) or true imperative Web Components.
* **API Paradigm**: Coralite uses a "Flat Options" API. All logic is defined within four strictly isolated blocks: `attributes`, `data`, `getters`, and `script`.
* **State Equation**: `Attributes + Data + Getters = State`. The template only sees this unified reactive proxy.

## 4. Static Components
Static components are purely HTML-based elements without scripting logic.
* **Definition**: Must be wrapped in a `<template id="component-name">` tag.
* **Usage**: The custom element tag name in the HTML page must exactly match the template's `id` (e.g., `<component-name></component-name>`).
* **Data Binding**: Data is passed via HTML attributes and rendered inside the template using double curly braces: `{{ attributeName }}`.

## 5. Dynamic Components (defineComponent)
Dynamic components are authored by appending a `<script type="module">` below the `<template>`, exporting the core `defineComponent` helper.

### 5.1 Anti-Patterns
When generating code for Coralite components, **DO NOT** use standard Web Component boilerplate.

**❌ BAD (Vanilla Web Component):**
```javascript
class MyComponent extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
  }
  connectedCallback() { ... }
}
customElements.define('my-component', MyComponent);
```

**✅ GOOD (Coralite defineComponent):**
```javascript
import { defineComponent } from 'coralite'
export default defineComponent({
  script: ({ state, root, signal, refs }) => { ... }
})
```

### 5.2 Component Structure: Isolated Blocks
A component definition consists of four primary blocks:

#### 1. `attributes` (Sync)
* **Purpose**: Coerces HTML attributes into JavaScript primitives.
* **Rule**: Strictly limited to primitives (`String`, `Number`, `Boolean`). `Object` and `Array` types are strictly blocked to avoid JSON-in-HTML parsing errors.
* **Reactivity**: Changes to HTML attributes (via `setAttribute`) automatically trigger updates to the unified `state`.

#### 2. `data({ state, page, ...context })` (Async/Server)
* **Purpose**: Fetches initial state on the server during the build process.
* **Arguments** (The Context Object):
  * `state`: Contains the component's resolved attributes (e.g., `state.category`). **Always use this to read HTML attribute values passed to the component.**
  * `page`: Contains global metadata about the consuming page/route (e.g., URL, file info). **Do NOT use this to look up component attributes.**
* **Serialization**: This block is fully stripped from the client bundle by the build tool. The returned object is serialized and merged into the unified client-side `state`.

**❌ BAD (Confusing Page Metadata with Component Attributes):**
```javascript
async data(context) {
    // Wrong: Trying to read a component attribute from global page metadata
    return { posts: await db.fetchPosts(context.page.category) }
}
```

**✅ GOOD (Reading Attributes from State):**
```javascript
async data({ state }) {
    // Correct: Reading the passed HTML attribute from the destructured state
    return { posts: await db.fetchPosts(state.category) }
}
```

#### 3. `getters` (Sync)
* **Purpose**: Pure functions for derived state (isomorphic).
* **Rule**: These receive a **Read-Only Proxy** of the state. They must never mutate state and cannot be `async`.
* **Reactivity**: Automatically re-evaluated when any underlying state dependency changes.

#### 4. `script({ state, root, signal, refs, ...pluginContext })` (Client)
* **Purpose**: The client-side controller for the component.
* **Arguments** (Injected via Client Context):
  * `state`: A **Read/Write Mutable Proxy**. Mutations here automatically trigger getter evaluations and precise DOM updates.
  * `root`: The custom element instance (the Light DOM node).
  * `signal`: An `AbortSignal` mapped to the component's lifecycle. Used for native cleanup (e.g., aborting fetch requests, removing global listeners) when disconnected.
  * `refs`: A dictionary of dynamically generated, globally unique IDs for elements marked with the `ref` attribute (provided by the Core `refs` plugin).
  * `...pluginContext`: **Extensible Boundary.** Any active Coralite plugin that augments the `client.context` will inject its attributes directly into this destructured argument object.
* **Usage**: All DOM events, imperative mutations, third-party library initializations, and side-effects live here.

### 5.3 The "Smart State, Dumb Template" Paradigm
HTML templates in Coralite are strictly declarative and "dumb". They do not support JavaScript logic or dot-notation. The template can *only* render flat keys from the unified `state`.

**Example: Standard Component**
```html
<template id="user-dashboard">
    <!-- State is rendered using double curly braces -->
    <div class="dashboard {{ theme }}">
        <p>Total Users: {{ userCount }}</p>
        <button ref="refresh">Toggle Theme</button>
    </div>
</template>

<script type="module">
import { defineComponent } from 'coralite'
import db from 'node:database' // Stripped from client bundle

export default defineComponent({
    // Attributes become part of state
    attributes: {
        theme: { type: String, default: 'light' }
    },
    // Data results become part of state
    async data({ state }) {
        return {
            // Note: If we needed the attribute, we would use state.theme here
            users: await db.fetchUsers()
        }
    },
    // Getters become part of state
    getters: {
        userCount: (state) => state.users.length
    },
    script: ({ state, root, signal, refs }) => {
        const btn = refs('refresh');
        btn.addEventListener('click', () => {
            state.theme = state.theme === 'light' ? 'dark' : 'light';
        }, { signal });
    }
})
</script>
```

### 5.4 Native Web APIs
* **Communication**: Use standard `CustomEvent` for component-to-component communication.

**Example: Dispatching a CustomEvent**
```javascript
import { defineComponent } from 'coralite'

export default defineComponent({
    script: ({ root }) => {
        root.dispatchEvent(new CustomEvent('theme-changed', {
            detail: { theme: 'dark' },
            bubbles: true,
            composed: true
        }));
    }
})
```

* **Cleanup**: Always use the provided `AbortSignal` (`signal`) to clean up event listeners and abort pending network requests when the component unmounts.

**Example: Aborting a Network Request with `signal`**
```javascript
import { defineComponent } from 'coralite'

export default defineComponent({
    script: async ({ signal }) => {
        try {
            const response = await fetch('/api/data', { signal });
            const data = await response.json();
            console.log(data);
        } catch (err) {
            if (err.name === 'AbortError') {
                console.log('Fetch aborted because component was disconnected.');
            } else {
                console.error('Fetch error:', err);
            }
        }
    }
})
```

### 5.5 Slots Projection & Transformation (`slots`)
You define transformations in the `slots` object. Each key is a slot name (use `'default'` for unnamed slots).
* **Signature**: `(slotNodes, state) => HTMLString | NodeArray | void`
* **Preservation**: Coralite moves original Light DOM elements rather than cloning them, preserving event listeners and internal state.

**Example: Slot Transformation**
```html
<template id="custom-layout">
    <div class="layout-container">
        <!-- The transformed 'header' slot is injected here -->
        <slot name="header"></slot>
        
        <main>
            <!-- The transformed default slot is injected here -->
            <slot></slot>
        </main>
    </div>
</template>

<script type="module">
import { defineComponent } from 'coralite'

export default defineComponent({
    attributes: { title: { type: String, default: 'My App' } },
    slots: {
        // Transform the default (unnamed) slot
        default: (slotNodes, state) => {
            return `<div class="content-wrapper">${slotNodes.map(n => n.data || '').join('')}</div>`;
        },
        // Transform a named slot
        header: (slotNodes, state) => {
            return `<header class="app-header"><h2>${state.title}</h2></header>`;
        }
    }
})
</script>
```

---

## 6. Framework Extensibility (definePlugin)
Plugins hook into the build lifecycle, provide custom components, or inject utilities into the client runtime.

### 6.1 The Plugin API Surface

The `definePlugin` function accepts an options object with the following schema:

* `name` (String): **Required**. The unique name of the plugin. This name acts as the virtual module import specifier for consuming the plugin inside components.
* `exports` (Object | Function): The core execution methods provided by the plugin. This uses **Two-Phase Currying** to explicitly receive the framework context, ensuring type safety and standard ES module ergonomics.
* `components` (Array): Paths to internal component `.html` files provided by the plugin to be parsed and made available to consumers.
* `server` (Function): A server-specific configuration/method block.
* `client` (Object): Configuration for the client runtime bundle.
* `config` (Object): A serializable configuration object accessible in the client bundle.
* `setup` (Function): A client-side setup function that executes when the client runtime initializes.
* `context` (Object): An object where each **key** represents the variable name to be injected into the component's `script` destructured argument. The **value** must be a **Two-Phase Curried Function**.
* `rootDir` (String): The root directory (auto-resolved to the caller directory if omitted).
* **Lifecycle Hooks** (Functions): `onPageSet`, `onPageUpdate`, `onPageDelete`, `onComponentSet`, `onComponentUpdate`, `onComponentDelete`, `onBeforePageRender`, `onAfterPageRender`, `onBeforeBuild`, `onAfterBuild`.

### 6.2 The `exports` System (Server-Side Virtual Modules)

The `exports` property defines methods available to components **during the server-side build process**. These are exposed via **Virtual Module Imports**.

#### 1. Core Rules for `exports`
*   **Top-Level Imports**: Plugins are imported at the top level of the component's `<script>` block using the plugin's `name` as the module specifier.
*   **Server-Only Execution**: Because these modules are virtual and server-side only, the imported functions **MUST** only be invoked within the `data()` block. The build tool strips these imports before sending the script to the client.
*   **Automatic Context Injection (Two-Phase Currying)**: Plugin authors write exports using currying. The engine automatically executes **Phase 1**, injecting the `CoralitePluginContext`. Component authors only see and call the **Phase 2** function.

#### 2. The Two-Phase Execution Flow (Internal)
-   **Phase 1 (Engine side):** The engine executes the outer function, passing the `CoralitePluginContext` (`state`, `page`, `module`, `root`).
-   **Phase 2 (Component side):** The engine provides the returned function to the component. This is what the developer invokes inside `data()`.

**Example: Defining the Plugin**

```javascript
import { definePlugin } from 'coralite'

export default definePlugin({
    // This unique name becomes the virtual module import specifier
    name: 'custom-logger-plugin',

    // 'exports' strictly uses Two-Phase Currying
    exports: {
        log: (context) => (message) => {
            // PHASE 1: Accessing component state and module info via explicit 'context'
            console.log(`[${context.module.name}] ${message}`, context.state);
            return { logged: true };
        },
        warn: (context) => (message) => {
            console.warn(`[WARNING on ${context.page.url}] ${message}`);
        }
    }
})

```

**Example: Consuming the Plugin in a Component**

```html
<template id="logger-demo">
    <div class="demo">
        <p>Build Status: {{ status }}</p>
    </div>
</template>

<script type="module">
import { defineComponent } from 'coralite'
// Import destructured methods directly using the plugin's name
import { log, warn } from 'custom-logger-plugin'

export default defineComponent({
    // Plugin exports MUST be used in the server-side data block
    async data({ state }) {
        // The context is already injected; simply call the Phase 2 function
        log('Component data is being fetched on the server!');

        if (!state.userId) {
            warn('No user ID was provided to this component.');
        }

        return {
            status: 'Built successfully'
        }
    },

    script: ({ refs, signal }) => {
        // Client-side interactions live here.
        // Server-only imports (like the logger) are stripped from this bundle.
    }
})
</script>
```

### 6.3 Two-Phase Currying & Injection
To ensure safety and isolation, client injections mapped in the `client.context` object use a curried setup:
  * **Phase 1 (Global Setup)**: The first function `(globalContext) =>` runs exactly once when the plugin is registered. **Best Practice:** Declare your plugin's shared state, caches, or singleton instances here. Do NOT declare them in the global module scope.
  * **Phase 2 (Local Instance)**: The returned function `(instanceContext) =>` runs every time a component using the plugin is mounted. **Best Practice:** Return the exact utility, value, or method meant for that single component instance here. The returned value maps directly to the defined key.

**Example: Advanced Plugin**
```javascript
import { definePlugin } from 'coralite'

export default definePlugin({
    name: 'state-plugin',
    client: {
        // context is an Object mapping keys to curried functions
        context: {
            $store: (globalContext) => {
                // PHASE 1: Plugin Setup
                // Shared state encapsulated to the plugin instance.
                const pluginStore = { theme: globalContext.config.theme }; 

                return (instanceContext) => {
                    // PHASE 2: Instance Injection
                    // Returns the actual value assigned to the `$store` key.
                    // Accessible via: script: ({ $store }) => { ... }
                    return pluginStore;
                }
            }
        },
        // Serializable client configuration
        config: { theme: 'dark' },
        // Executes once when the client runtime initializes
        setup: () => console.log('Plugin client runtime initialized.')
    },
    // Paths to plugin-specific HTML components
    components: ['src/components/plugin-widget.html'],
    // Server-side hook
    onBeforeBuild: async function() {
        console.log(`Starting build with plugin: ${this.name}`);
    }
})
```

## 7. Built-In Plugins
* **Metadata Plugin** (`metadataPlugin`): Extracts SEO data during the build phase. Maps `<html lang>` to `context.page.meta.lang`, `<title>` to `state.page_title`, and standard meta tags like `<meta name="viewport">` to `state.meta_viewport`.
* **Refs Plugin** (`refsPlugin`): Provides a `refs` dictionary function directly within the `script` context (`script: ({ refs }) => {}`). It enables scoped, collision-free element selection across component instances by resolving dynamically generated unique IDs for elements marked with the `ref` attribute.
  * **Usage**: In your template, add the attribute `<button ref="my-btn">`. In your script, select it via `const btn = refs('my-btn');`.
* **Static Asset Plugin** (`staticAssetPlugin`): Copies static assets to the build output folder during `onBeforeBuild`. You pass an array of asset configuration objects.
  * **Usage**: `staticAssetPlugin([{ src: 'public/img.png', dest: 'img.png' }, { pkg: 'some-npm-pkg', path: 'dist/styles.css', dest: 'styles.css' }])`
* **Testing Plugin** (`testingPlugin`): Automates E2E testing preparation. It traverses the server-side AST during compilation and automatically duplicates all `ref` attributes to `data-testid` attributes, allowing tests to select DOM nodes without manually writing `data-testid` properties in templates.

## 8. E2E Testing
* **Readiness**: Await `window.__coralite_ready__` after navigation.
* **Selectors**: Rely on `data-testid` generated by the **Testing Plugin** for robust selectors.
