Files
michaelschiemer/docs/livecomponents/api-reference.md
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

24 KiB

LiveComponents API Reference

Complete API Documentation for LiveComponents System

This reference covers all public classes, methods, attributes, and interfaces for building LiveComponents applications.


Table of Contents

  1. LiveComponent Base Class
  2. Attributes
  3. HTTP Integration
  4. JavaScript API
  5. Value Objects
  6. Services
  7. Events

LiveComponent Base Class

abstract class LiveComponent

Base class for all LiveComponents. Provides lifecycle hooks, rendering, and state management.

Properties

/**
 * Unique component instance ID
 * Auto-generated on component creation
 */
protected readonly string $id;

/**
 * Component name (class name without namespace)
 */
protected readonly string $name;

Lifecycle Methods

public function mount(array $initialData = []): void

Called when component is first created.

Parameters:

  • $initialData - Initial data passed to component

Usage:

public function mount(array $initialData = []): void
{
    $this->userId = $initialData['user_id'] ?? null;
    $this->loadUserData();
    $this->enableSse(); // Enable real-time updates
}

public function hydrate(array $state): void

Called on subsequent requests to restore component state.

Parameters:

  • $state - Serialized component state from client

Usage:

public function hydrate(array $state): void
{
    // State automatically restored to #[LiveProp] properties
    // Override for custom hydration logic
    parent::hydrate($state);
    $this->recalculateDerivedState();
}

public function updated(string $property, mixed $oldValue, mixed $newValue): void

Called when a property is updated via two-way binding.

Parameters:

  • $property - Property name that changed
  • $oldValue - Previous value
  • $newValue - New value

Usage:

public function updated(string $property, mixed $oldValue, mixed $newValue): void
{
    if ($property === 'searchTerm' && strlen($newValue) >= 3) {
        $this->performSearch();
    }
}

public function unmount(): void

Called when component is destroyed.

Usage:

public function unmount(): void
{
    // Clean up resources
    $this->closeConnections();
    $this->clearCache();
}

Rendering

abstract public function render(): string

Render component to HTML.

Returns: HTML string

Usage:

public function render(): string
{
    return $this->view('components/user-profile', [
        'user' => $this->user,
        'posts' => $this->posts
    ]);
}

protected function view(string $template, array $data = []): string

Render a template with data.

Parameters:

  • $template - Template path (relative to views directory)
  • $data - Data to pass to template

Returns: Rendered HTML

Usage:

protected function render(): string
{
    return $this->view('components/product-list', [
        'products' => $this->products,
        'totalCount' => $this->totalCount
    ]);
}

State Management

protected function getState(): array

Get component state for serialization.

Returns: Associative array of state

Usage:

protected function getState(): array
{
    return [
        'userId' => $this->userId,
        'filters' => $this->filters,
        'page' => $this->page
    ];
}

protected function setState(array $state): void

Restore component state.

Parameters:

  • $state - State array to restore

Real-Time Updates

protected function enableSse(array $channels = []): void

Enable Server-Sent Events for real-time updates.

Parameters:

  • $channels - Channels to subscribe to (default: component-specific channel)

Usage:

public function mount(): void
{
    $this->enableSse([
        'user.updates',
        "team.{$this->teamId}.activity"
    ]);
}

protected function broadcast(string $channel, array $data): void

Broadcast update to all clients listening on channel.

Parameters:

  • $channel - Channel name
  • $data - Data to broadcast

Usage:

#[LiveAction]
public function updateStatus(string $status): void
{
    $this->status = $status;

    // Broadcast to all team members
    $this->broadcast("team.{$this->teamId}.updates", [
        'type' => 'status_changed',
        'user' => $this->user->name,
        'status' => $status
    ]);
}

Static Factory

public static function mount(string $componentClass, array $initialData = []): string

Create and render component.

Parameters:

  • $componentClass - Fully qualified class name
  • $initialData - Initial data for component

Returns: Rendered component HTML

Usage:

// In controller
public function dashboard(): ViewResult
{
    return new ViewResult('dashboard', [
        'statsWidget' => LiveComponent::mount(StatsWidget::class, [
            'user_id' => $currentUser->id
        ])
    ]);
}

Attributes

#[LiveProp]

Marks a property for automatic state synchronization.

Usage:

use App\Framework\LiveComponents\Attributes\LiveProp;

final class SearchComponent extends LiveComponent
{
    #[LiveProp]
    public string $searchTerm = '';

    #[LiveProp]
    public array $filters = [];

    #[LiveProp]
    public int $page = 1;
}

Options:

  • Properties are automatically serialized/deserialized
  • Two-way binding with data-lc-model
  • State preserved across requests

#[LiveAction]

Marks a method as a LiveComponent action.

Usage:

use App\Framework\LiveComponents\Attributes\LiveAction;

#[LiveAction]
public function search(): void
{
    $this->results = $this->searchService->search($this->searchTerm);
}

#[LiveAction]
public function addToCart(string $productId): void
{
    $this->cart->add($productId);
}

Callable From Client:

<button data-lc-action="search">Search</button>
<button
    data-lc-action="addToCart"
    data-lc-params='{"productId": "123"}'
>
    Add to Cart
</button>

#[Fragment(string|array $names)]

Marks action to only update specific fragments.

Usage:

use App\Framework\LiveComponents\Attributes\Fragment;

#[LiveAction]
#[Fragment('search-results')]
public function search(): void
{
    $this->results = $this->searchService->search($this->searchTerm);
    // Only search-results fragment re-renders
}

#[LiveAction]
#[Fragment(['user-stats', 'activity-feed'])]
public function refreshDashboard(): void
{
    $this->updateStats();
    $this->loadActivity();
    // Both fragments re-render
}

#[RateLimit(int $requests, int $window)]

Apply rate limiting to component or action.

Usage:

use App\Framework\LiveComponents\Attributes\RateLimit;

#[RateLimit(requests: 10, window: 60)]
final class SearchComponent extends LiveComponent
{
    #[LiveAction]
    #[RateLimit(requests: 5, window: 60)]
    public function expensiveSearch(): void
    {
        // Limited to 5 requests per minute
    }
}

#[Authorize(array $roles = [], array $permissions = [])]

Require authorization for action.

Usage:

use App\Framework\LiveComponents\Attributes\Authorize;

#[LiveAction]
#[Authorize(roles: ['admin'])]
public function deleteUser(string $userId): void
{
    $this->userService->delete($userId);
}

#[LiveAction]
#[Authorize(permissions: ['users.edit'])]
public function updateUserRole(string $userId, string $role): void
{
    $this->userService->updateRole($userId, $role);
}

#[Idempotent]

Ensure action executes exactly once (for critical operations).

Usage:

use App\Framework\LiveComponents\Attributes\Idempotent;

#[LiveAction]
#[Idempotent]
public function processPayment(): void
{
    $this->paymentGateway->charge($this->amount);
    $this->orderService->createOrder($this->cartItems);
    // Guaranteed to execute only once even if request duplicated
}

#[Encrypted]

Encrypt property value in client state.

Usage:

use App\Framework\LiveComponents\Attributes\Encrypted;

final class PaymentForm extends LiveComponent
{
    #[LiveProp]
    #[Encrypted]
    public string $creditCardNumber = '';

    #[LiveProp]
    #[Encrypted]
    public string $cvv = '';

    #[LiveProp] // Not encrypted - safe to expose
    public string $cardholderName = '';
}

#[Optimistic(string $property, string $operation)]

Enable optimistic UI updates for action.

Usage:

use App\Framework\LiveComponents\Attributes\Optimistic;

#[LiveAction]
#[Optimistic(property: 'isLiked', operation: 'toggle')]
#[Optimistic(property: 'likeCount', operation: 'increment')]
public function toggleLike(): void
{
    $this->isLiked = !$this->isLiked;
    $this->likeCount += $this->isLiked ? 1 : -1;
}

Operations:

  • toggle - Boolean toggle
  • increment - Increment number
  • decrement - Decrement number
  • set - Set to specific value

HTTP Integration

LiveComponent HTTP Endpoint

POST /livecomponent/{componentId}/{action}

Execute a LiveComponent action.

Request Headers:

Content-Type: application/json
X-CSRF-Token: {token}
X-Component-Checksum: {checksum}

Request Body:

{
    "state": {
        "searchTerm": "laptop",
        "filters": ["electronics"],
        "page": 1
    },
    "params": {
        "productId": "123"
    }
}

Response:

{
    "success": true,
    "html": "<div data-component-id=\"abc123\">...</div>",
    "fragments": {
        "search-results": "<div data-lc-fragment=\"search-results\">...</div>"
    },
    "redirect": null,
    "events": []
}

Batch Endpoint

POST /livecomponent/{componentId}/batch

Execute multiple actions in single request.

Request Body:

{
    "batch": [
        {
            "action": "incrementCounter",
            "params": {}
        },
        {
            "action": "updateName",
            "params": {"name": "John"}
        },
        {
            "action": "save",
            "params": {}
        }
    ],
    "state": {
        "count": 0,
        "name": ""
    }
}

Response:

{
    "success": true,
    "results": [
        { "success": true, "html": "..." },
        { "success": true, "html": "..." },
        { "success": true, "html": "..." }
    ]
}

Upload Endpoint

POST /livecomponent/{componentId}/upload

Upload file chunks.

Request Headers:

Content-Type: multipart/form-data
X-Upload-ID: {unique-upload-id}
X-Chunk-Index: {chunk-number}
X-Total-Chunks: {total-chunks}

Response:

{
    "success": true,
    "chunk_index": 0,
    "received_bytes": 1048576,
    "complete": false
}

SSE Endpoint

GET /livecomponent/sse/{componentId}

Subscribe to Server-Sent Events.

Response (text/event-stream):

event: component-update
data: {"type":"fragment","fragment":"user-stats","html":"<div>...</div>"}

event: component-update
data: {"type":"full","html":"<div data-component-id=\"abc123\">...</div>"}

event: heartbeat
data: {"timestamp":1634567890}

JavaScript API

LiveComponent Global Object

Main JavaScript interface for LiveComponents.

Methods

LiveComponent.mount(element: HTMLElement): void

Initialize component on element.

Usage:

const componentEl = document.querySelector('[data-component-id="abc123"]');
LiveComponent.mount(componentEl);

LiveComponent.unmount(componentId: string): void

Destroy component and clean up resources.

Usage:

LiveComponent.unmount('abc123');

LiveComponent.executeAction(componentId: string, action: string, params: object): Promise<void>

Manually execute component action.

Usage:

await LiveComponent.executeAction('abc123', 'search', {
    query: 'laptops'
});

LiveComponent.getComponent(componentId: string): Component | null

Get component instance.

Returns: Component object or null

Usage:

const component = LiveComponent.getComponent('abc123');

console.log(component.state);
console.log(component.name);

LiveComponent.getState(componentId: string): object | null

Get component state.

Returns: State object or null

Usage:

const state = LiveComponent.getState('abc123');

console.log(state.searchTerm);
console.log(state.filters);

LiveComponent.setState(componentId: string, state: object): void

Update component state (triggers server sync).

Usage:

LiveComponent.setState('abc123', {
    searchTerm: 'new query',
    page: 2
});

LiveComponent.refresh(componentId: string): Promise<void>

Force component refresh from server.

Usage:

await LiveComponent.refresh('abc123');

LiveComponent.configure(options: object): void

Configure global LiveComponent settings.

Options:

  • batchSize - Max actions per batch (default: 10)
  • batchDebounce - Debounce delay in ms (default: 50)
  • optimisticUI - Enable optimistic updates (default: true)
  • csrfProtection - Enable CSRF protection (default: true)
  • enableSSE - Enable Server-Sent Events (default: true)

Usage:

LiveComponent.configure({
    batchSize: 15,
    batchDebounce: 100,
    optimisticUI: true
});

Properties

LiveComponent.components: Map<string, Component>

All active components.

Usage:

console.log(`Active components: ${LiveComponent.components.size}`);

LiveComponent.components.forEach((component, id) => {
    console.log(`${id}: ${component.name}`);
});

Component Object

Individual component instance (returned by getComponent()).

Properties

interface Component {
    id: string;              // Unique component ID
    name: string;            // Component class name
    state: object;           // Current state
    element: HTMLElement;    // DOM element
    isConnected: boolean;    // SSE connection status
    isPending: boolean;      // Has pending actions
}

Data Attributes

data-component-id

Identifies component root element. Required on component container.

<div data-component-id="{component_id}" data-component-name="SearchComponent">
    <!-- Component content -->
</div>

data-lc-model

Two-way data binding for inputs.

<input
    type="text"
    data-lc-model="searchTerm"
    value="{searchTerm}"
/>

<select data-lc-model="category">
    <option value="electronics">Electronics</option>
    <option value="books">Books</option>
</select>

<input
    type="checkbox"
    data-lc-model="agreed"
    {agreed ? 'checked' : ''}
/>

Modifiers:

  • data-lc-model.debounce.500="searchTerm" - Debounce 500ms
  • data-lc-model.lazy="bio" - Update on blur instead of input

data-lc-action

Trigger action on click.

<button data-lc-action="search">Search</button>

<button
    data-lc-action="addToCart"
    data-lc-params='{"productId": "123"}'
>
    Add to Cart
</button>

data-lc-fragment

Mark fragment for partial updates.

<div data-lc-fragment="search-results">
    <!-- Only this section updates -->
    <for items="results" as="result">
        <div>{result.title}</div>
    </for>
</div>

data-lc-submit

Handle form submission.

<form data-lc-submit="handleSubmit">
    <input type="text" name="email" />
    <button type="submit">Submit</button>
</form>

data-optimistic

Enable optimistic update for action.

<button
    data-lc-action="toggleLike"
    data-optimistic="true"
>
    {isLiked ? '❤️' : '🤍'} Like
</button>

data-lc-upload

Enable chunked file upload.

<input
    type="file"
    data-lc-upload="handleFileUpload"
    data-chunk-size="1048576"
    data-max-file-size="10485760"
/>

<div data-upload-progress>
    <div class="progress-bar" style="width: {uploadProgress}%"></div>
    <span>{uploadProgress}%</span>
</div>

Value Objects

ComponentId

Unique component identifier.

final readonly class ComponentId
{
    public function __construct(public string $value) {}

    public static function generate(): self
    {
        return new self(bin2hex(random_bytes(16)));
    }

    public function toString(): string
    {
        return $this->value;
    }
}

ComponentState

Component state snapshot.

final readonly class ComponentState
{
    public function __construct(
        public array $properties,
        public string $checksum
    ) {}

    public function toArray(): array
    {
        return $this->properties;
    }

    public function verify(string $checksum): bool
    {
        return hash_equals($this->checksum, $checksum);
    }
}

FragmentName

Fragment identifier.

final readonly class FragmentName
{
    public function __construct(public string $value) {}

    public static function fromString(string $name): self
    {
        return new self($name);
    }
}

Services

LiveComponentManager

Core service for component lifecycle management.

final readonly class LiveComponentManager
{
    public function create(
        string $componentClass,
        array $initialData = []
    ): LiveComponent;

    public function hydrate(
        string $componentClass,
        ComponentId $id,
        ComponentState $state
    ): LiveComponent;

    public function executeAction(
        LiveComponent $component,
        string $actionName,
        array $params = []
    ): ActionResult;

    public function render(LiveComponent $component): string;

    public function renderFragment(
        LiveComponent $component,
        FragmentName $fragment
    ): string;
}

LiveComponentState

State management service.

final readonly class LiveComponentState
{
    public function serialize(LiveComponent $component): ComponentState;

    public function deserialize(
        string $componentClass,
        array $state
    ): LiveComponent;

    public function generateChecksum(array $state): string;

    public function verifyChecksum(array $state, string $checksum): bool;
}

Events

Client-Side Events

livecomponent:mount

Fired when component is mounted.

window.addEventListener('livecomponent:mount', (e) => {
    console.log('Component mounted:', e.detail);
    // { componentId, name, element }
});

livecomponent:action-start

Fired when action starts.

window.addEventListener('livecomponent:action-start', (e) => {
    console.log('Action starting:', e.detail);
    // { componentId, action, params, optimistic }
});

livecomponent:action-executed

Fired when action completes.

window.addEventListener('livecomponent:action-executed', (e) => {
    console.log('Action completed:', e.detail);
    // { componentId, action, duration, success }
});

livecomponent:action-error

Fired when action fails.

window.addEventListener('livecomponent:action-error', (e) => {
    console.error('Action failed:', e.detail);
    // { componentId, action, error, response }
});

livecomponent:fragment-updated

Fired when fragment updates.

window.addEventListener('livecomponent:fragment-updated', (e) => {
    console.log('Fragment updated:', e.detail);
    // { componentId, fragmentName, duration, nodesChanged }
});

livecomponent:batch-sent

Fired when batch request is sent.

window.addEventListener('livecomponent:batch-sent', (e) => {
    console.log('Batch sent:', e.detail);
    // { componentId, actionsCount, payloadSize }
});

livecomponent:optimistic-applied

Fired when optimistic update is applied.

window.addEventListener('livecomponent:optimistic-applied', (e) => {
    console.log('Optimistic update:', e.detail);
    // { componentId, action, stateBeforeOptimistic }
});

livecomponent:optimistic-rollback

Fired when optimistic update is rolled back.

window.addEventListener('livecomponent:optimistic-rollback', (e) => {
    console.warn('Optimistic rollback:', e.detail);
    // { componentId, action, error, stateBeforeOptimistic }
});

livecomponent:sse-connected

Fired when SSE connection established.

window.addEventListener('livecomponent:sse-connected', (e) => {
    console.log('SSE connected:', e.detail);
    // { componentId, channels }
});

livecomponent:sse-disconnected

Fired when SSE connection lost.

window.addEventListener('livecomponent:sse-disconnected', (e) => {
    console.warn('SSE disconnected:', e.detail);
    // { componentId, reason }
});

livecomponent:error

Fired on any LiveComponent error.

window.addEventListener('livecomponent:error', (e) => {
    console.error('LiveComponent error:', e.detail);
    // { componentId, type, message, code }

    if (e.detail.code === 'CSRF_TOKEN_EXPIRED') {
        LiveComponent.refreshCsrfToken();
    }
});

Server-Side Events

Events dispatched during server-side processing (integrate with framework Event System).

ComponentMountedEvent

final readonly class ComponentMountedEvent
{
    public function __construct(
        public string $componentClass,
        public ComponentId $componentId,
        public array $initialData
    ) {}
}

ComponentActionExecutedEvent

final readonly class ComponentActionExecutedEvent
{
    public function __construct(
        public string $componentClass,
        public ComponentId $componentId,
        public string $actionName,
        public array $params,
        public float $duration,
        public bool $success
    ) {}
}

ComponentUnmountedEvent

final readonly class ComponentUnmountedEvent
{
    public function __construct(
        public string $componentClass,
        public ComponentId $componentId
    ) {}
}

Error Handling

Common Errors

ComponentNotFoundException

Component class not found.

throw ComponentNotFoundException::forClass('App\\Components\\NonExistent');

ActionNotFoundException

Action method not found on component.

throw ActionNotFoundException::forAction('nonExistentAction', $componentClass);

InvalidStateException

Component state invalid or corrupted.

throw InvalidStateException::checksumMismatch($expectedChecksum, $actualChecksum);

RateLimitExceededException

Rate limit exceeded for component/action.

throw RateLimitExceededException::forAction($actionName, $retryAfter);

UnauthorizedException

User not authorized for action.

throw UnauthorizedException::missingRole($requiredRole);
throw UnauthorizedException::missingPermission($requiredPermission);

Type Definitions

TypeScript Definitions

For TypeScript projects:

// livecomponents.d.ts
declare global {
    interface Window {
        LiveComponent: {
            mount(element: HTMLElement): void;
            unmount(componentId: string): void;
            executeAction(
                componentId: string,
                action: string,
                params?: object
            ): Promise<void>;
            getComponent(componentId: string): Component | null;
            getState(componentId: string): object | null;
            setState(componentId: string, state: object): void;
            refresh(componentId: string): Promise<void>;
            configure(options: LiveComponentConfig): void;
            components: Map<string, Component>;
        };
    }

    interface Component {
        id: string;
        name: string;
        state: object;
        element: HTMLElement;
        isConnected: boolean;
        isPending: boolean;
    }

    interface LiveComponentConfig {
        batchSize?: number;
        batchDebounce?: number;
        optimisticUI?: boolean;
        csrfProtection?: boolean;
        enableSSE?: boolean;
    }
}

export {};

Next: Advanced Features