- 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.
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
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 toggleincrement- Increment numberdecrement- Decrement numberset- 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 500msdata-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 →