CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-microsoft--fast-element

A library for constructing Web Components

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

ssr-hydration.mddocs/

SSR Hydration

Server-side rendering support with hydration capabilities for fast initial page loads and SEO optimization, enabling isomorphic application development.

Capabilities

Hydration Markup

Utilities for generating and processing hydration markers that enable client-side hydration of server-rendered content.

/**
 * Utilities for hydration markup generation and processing
 */
const HydrationMarkup: {
  /** The attribute name used to mark hydratable elements */
  readonly attributeMarkerName: string;
  
  /**
   * Creates a start marker for content binding hydration
   * @param index - The binding index
   * @param targetNodeId - The ID of the target node
   * @returns HTML comment marker string
   */
  contentBindingStartMarker(index: number, targetNodeId: string): string;
  
  /**
   * Creates an end marker for content binding hydration
   * @param index - The binding index
   * @returns HTML comment marker string
   */
  contentBindingEndMarker(index: number): string;
  
  /**
   * Creates a marker for attribute binding hydration
   * @param index - The binding index
   * @param targetNodeId - The ID of the target node
   * @param attributeName - The name of the attribute
   * @returns HTML comment marker string
   */
  attributeBindingMarker(index: number, targetNodeId: string, attributeName: string): string;
  
  /**
   * Parses hydration markers from HTML content
   * @param html - The HTML content to parse
   * @returns Parsed hydration information
   */
  parseHydrationMarkers(html: string): HydrationInfo;
};

/**
 * Hydration information extracted from markers
 */
interface HydrationInfo {
  /** Content binding locations */
  contentBindings: Array<{
    index: number;
    targetNodeId: string;
    startNode: Comment;
    endNode: Comment;
  }>;
  
  /** Attribute binding locations */
  attributeBindings: Array<{
    index: number;
    targetNodeId: string;
    attributeName: string;
    marker: Comment;
  }>;
  
  /** Hydratable elements */
  hydratableElements: Array<{
    element: Element;
    templateId: string;
  }>;
}

Usage Examples:

import { ViewTemplate, html } from "@microsoft/fast-element";
import { HydrationMarkup } from "@microsoft/fast-element/element-hydration.js";

// Template that supports hydration
const userCardTemplate = html<UserCard>`
  <div class="user-card" ${HydrationMarkup.attributeMarker('data-user-id', x => x.userId)}>
    ${HydrationMarkup.contentBindingStartMarker(0, 'user-name')}
    <h2 id="user-name">${x => x.name}</h2>
    ${HydrationMarkup.contentBindingEndMarker(0)}
    
    ${HydrationMarkup.contentBindingStartMarker(1, 'user-email')}
    <p id="user-email">${x => x.email}</p>
    ${HydrationMarkup.contentBindingEndMarker(1)}
    
    <div class="user-stats">
      ${HydrationMarkup.contentBindingStartMarker(2, 'post-count')}
      <span id="post-count">Posts: ${x => x.postCount}</span>
      ${HydrationMarkup.contentBindingEndMarker(2)}
      
      ${HydrationMarkup.contentBindingStartMarker(3, 'follower-count')}  
      <span id="follower-count">Followers: ${x => x.followerCount}</span>
      ${HydrationMarkup.contentBindingEndMarker(3)}
    </div>
  </div>
`;

// Server-side rendering function
function renderUserCardSSR(userData: UserData): string {
  // Server-side template rendering with hydration markers
  let html = `
    <div class="user-card" data-user-id="${userData.userId}" ${HydrationMarkup.attributeMarkerName}="user-card-template">
      ${HydrationMarkup.contentBindingStartMarker(0, 'user-name')}
      <h2 id="user-name">${userData.name}</h2>
      ${HydrationMarkup.contentBindingEndMarker(0)}
      
      ${HydrationMarkup.contentBindingStartMarker(1, 'user-email')}
      <p id="user-email">${userData.email}</p>
      ${HydrationMarkup.contentBindingEndMarker(1)}
      
      <div class="user-stats">
        ${HydrationMarkup.contentBindingStartMarker(2, 'post-count')}
        <span id="post-count">Posts: ${userData.postCount}</span>
        ${HydrationMarkup.contentBindingEndMarker(2)}
        
        ${HydrationMarkup.contentBindingStartMarker(3, 'follower-count')}
        <span id="follower-count">Followers: ${userData.followerCount}</span>
        ${HydrationMarkup.contentBindingEndMarker(3)}
      </div>
    </div>
  `;
  
  return html;
}

// Client-side hydration process
class HydrationManager {
  static hydrateUserCard(element: Element, userData: UserData): UserCard {
    // Parse hydration info
    const hydrationInfo = HydrationMarkup.parseHydrationMarkers(element.outerHTML);
    
    // Create component instance
    const userCard = new UserCard();
    userCard.userId = userData.userId;
    userCard.name = userData.name;
    userCard.email = userData.email;
    userCard.postCount = userData.postCount;
    userCard.followerCount = userData.followerCount;
    
    // Hydrate the element
    this.hydrateElement(userCard, element, hydrationInfo);
    
    return userCard;
  }
  
  private static hydrateElement(
    component: any, 
    element: Element, 
    hydrationInfo: HydrationInfo
  ): void {
    // Process content bindings
    hydrationInfo.contentBindings.forEach(binding => {
      const targetElement = element.querySelector(`#${binding.targetNodeId}`);
      if (targetElement) {
        // Establish reactive binding for content
        this.establishContentBinding(component, targetElement, binding.index);
      }
    });
    
    // Process attribute bindings  
    hydrationInfo.attributeBindings.forEach(binding => {
      const targetElement = element.querySelector(`#${binding.targetNodeId}`);
      if (targetElement) {
        // Establish reactive binding for attribute
        this.establishAttributeBinding(
          component, 
          targetElement, 
          binding.attributeName, 
          binding.index
        );
      }
    });
  }
  
  private static establishContentBinding(
    component: any, 
    element: Element, 
    bindingIndex: number
  ): void {
    // Set up reactive content binding
    Observable.getNotifier(component).subscribe({
      handleChange: () => {
        // Update content based on component state
        this.updateElementContent(component, element, bindingIndex);
      }
    });
  }
  
  private static establishAttributeBinding(
    component: any, 
    element: Element, 
    attributeName: string,
    bindingIndex: number
  ): void {
    // Set up reactive attribute binding
    Observable.getNotifier(component).subscribe({
      handleChange: () => {
        // Update attribute based on component state
        this.updateElementAttribute(component, element, attributeName, bindingIndex);
      }
    });
  }
  
  private static updateElementContent(
    component: any, 
    element: Element, 
    bindingIndex: number
  ): void {
    // Update logic based on binding index and component state
    switch (bindingIndex) {
      case 0: // User name
        element.textContent = component.name;
        break;
      case 1: // User email
        element.textContent = component.email;
        break;
      case 2: // Post count
        element.textContent = `Posts: ${component.postCount}`;
        break;
      case 3: // Follower count
        element.textContent = `Followers: ${component.followerCount}`;
        break;
    }
  }
  
  private static updateElementAttribute(
    component: any, 
    element: Element, 
    attributeName: string,
    bindingIndex: number
  ): void {
    // Update attribute based on component state
    if (attributeName === 'data-user-id') {
      element.setAttribute(attributeName, component.userId);
    }
  }
}

interface UserData {
  userId: string;
  name: string;
  email: string;
  postCount: number;
  followerCount: number;
}

class UserCard {
  userId: string = '';
  name: string = '';
  email: string = '';
  postCount: number = 0;
  followerCount: number = 0;
}

Hydratable Templates

Template interfaces and implementations that support server-side rendering and client-side hydration.

/**
 * Checks if an object supports hydration
 * @param obj - The object to check
 * @returns True if the object is hydratable
 */
function isHydratable(obj: any): obj is { [Hydratable]: true };

/**
 * Symbol that marks an object as hydratable
 */
const Hydratable: unique symbol;

/**
 * Element view template that supports hydration
 */
interface HydratableElementViewTemplate<TSource = any, TParent = any>
  extends ElementViewTemplate<TSource, TParent> {
  
  /**
   * Hydrates an element view from server-rendered content
   * @param firstChild - First child node of the hydration range
   * @param lastChild - Last child node of the hydration range
   * @param hostBindingTarget - The element that host behaviors will be bound to
   */
  hydrate(
    firstChild: Node,
    lastChild: Node,
    hostBindingTarget?: Element
  ): ElementView<TSource, TParent>;
}

/**
 * Synthetic view template that supports hydration
 */
interface HydratableSyntheticViewTemplate<TSource = any, TParent = any>
  extends SyntheticViewTemplate<TSource, TParent> {
  
  /**
   * Hydrates a synthetic view from server-rendered content
   * @param firstChild - First child node of the hydration range
   * @param lastChild - Last child node of the hydration range
   */
  hydrate(firstChild: Node, lastChild: Node): SyntheticView<TSource, TParent>;
}

/**
 * View interface that supports hydration
 */
interface HydratableView extends HTMLView {
  /** Indicates this view supports hydration */
  readonly isHydratable: true;
  
  /**
   * Hydrates the view from existing DOM content
   * @param firstChild - First child node to hydrate
   * @param lastChild - Last child node to hydrate
   */
  hydrate(firstChild: Node, lastChild: Node): void;
}

Usage Examples:

import { 
  isHydratable, 
  Hydratable, 
  HydratableElementViewTemplate,
  ViewTemplate,
  html,
  FASTElement,
  customElement
} from "@microsoft/fast-element";

// Create hydratable template
function createHydratableTemplate<T>(): HydratableElementViewTemplate<T> {
  const template = html<T>`
    <div class="hydratable-content">
      <h1>${x => (x as any).title}</h1>
      <p>${x => (x as any).description}</p>
    </div>
  `;
  
  // Mark template as hydratable
  (template as any)[Hydratable] = true;
  
  // Add hydration method
  (template as any).hydrate = function(
    firstChild: Node,
    lastChild: Node,
    hostBindingTarget?: Element
  ) {
    // Create view from existing DOM
    const view = this.create(hostBindingTarget);
    
    // Hydrate from existing nodes
    view.hydrate(firstChild, lastChild);
    
    return view;
  };
  
  return template as HydratableElementViewTemplate<T>;
}

// Hydratable component
@customElement("hydratable-component")
export class HydratableComponent extends FASTElement {
  title: string = "Default Title";
  description: string = "Default Description";
  
  // Use hydratable template
  static template = createHydratableTemplate<HydratableComponent>();
  
  // Support SSR hydration
  static supportsHydration = true;
  
  connectedCallback() {
    super.connectedCallback();
    
    // Check if this component needs hydration
    if (this.needsHydration()) {
      this.performHydration();
    }
  }
  
  private needsHydration(): boolean {
    // Check for hydration markers or server-rendered content
    return this.hasAttribute('data-ssr') || 
           this.querySelector('[data-hydration-marker]') !== null;
  }
  
  private performHydration(): void {
    if (isHydratable(HydratableComponent.template)) {
      // Get existing content range
      const firstChild = this.shadowRoot?.firstChild;
      const lastChild = this.shadowRoot?.lastChild;
      
      if (firstChild && lastChild) {
        // Perform hydration
        const view = HydratableComponent.template.hydrate(
          firstChild,
          lastChild,
          this
        );
        
        // Bind to current component data
        view.bind(this);
      }
    }
  }
}

// SSR rendering utility
class SSRRenderer {
  static renderComponentToString<T>(
    template: HydratableElementViewTemplate<T>,
    data: T
  ): string {
    if (!isHydratable(template)) {
      throw new Error('Template must be hydratable for SSR');
    }
    
    // Server-side rendering logic
    return this.renderTemplateWithData(template, data);
  }
  
  private static renderTemplateWithData<T>(
    template: HydratableElementViewTemplate<T>,
    data: T
  ): string {
    // Simplified SSR rendering
    // In real implementation, this would involve proper template processing
    
    const title = (data as any).title || 'Default Title';
    const description = (data as any).description || 'Default Description';
    
    return `
      <div class="hydratable-content" data-ssr="true">
        <!-- ${HydrationMarkup.contentBindingStartMarker(0, 'title')} -->
        <h1>${title}</h1>
        <!-- ${HydrationMarkup.contentBindingEndMarker(0)} -->
        
        <!-- ${HydrationMarkup.contentBindingStartMarker(1, 'description')} -->
        <p>${description}</p>
        <!-- ${HydrationMarkup.contentBindingEndMarker(1)} -->
      </div>
    `;
  }
}

// Client-side hydration orchestrator
class HydrationOrchestrator {
  private hydrationQueue: Array<{
    element: Element;
    component: any;
    template: HydratableElementViewTemplate<any>;
  }> = [];
  
  queueForHydration<T>(
    element: Element,
    component: T,
    template: HydratableElementViewTemplate<T>
  ): void {
    if (!isHydratable(template)) {
      console.warn('Template is not hydratable, skipping hydration queue');
      return;
    }
    
    this.hydrationQueue.push({ element, component, template });
  }
  
  async processHydrationQueue(): Promise<void> {
    for (const item of this.hydrationQueue) {
      try {
        await this.hydrateComponent(item);
      } catch (error) {
        console.error('Hydration failed for component:', error);
      }
    }
    
    this.hydrationQueue = [];
  }
  
  private async hydrateComponent(item: {
    element: Element;
    component: any;
    template: HydratableElementViewTemplate<any>;
  }): Promise<void> {
    const { element, component, template } = item;
    
    // Find hydration range
    const shadowRoot = element.shadowRoot;
    if (!shadowRoot) {
      throw new Error('Element must have shadow root for hydration');
    }
    
    const firstChild = shadowRoot.firstChild;
    const lastChild = shadowRoot.lastChild;
    
    if (firstChild && lastChild) {
      // Perform hydration
      const view = template.hydrate(firstChild, lastChild, element);
      
      // Bind to component data
      view.bind(component);
      
      // Mark as hydrated
      element.setAttribute('data-hydrated', 'true');
    }
  }
}

// Application setup with SSR hydration
class SSRApp {
  private hydrationOrchestrator = new HydrationOrchestrator();
  
  async initializeWithHydration(): Promise<void> {
    // Find all server-rendered components
    const ssrElements = document.querySelectorAll('[data-ssr]');
    
    for (const element of ssrElements) {
      await this.setupComponentHydration(element);
    }
    
    // Process all queued hydrations
    await this.hydrationOrchestrator.processHydrationQueue();
  }
  
  private async setupComponentHydration(element: Element): Promise<void> {
    const tagName = element.tagName.toLowerCase();
    
    // Map element to component class and template
    const componentInfo = this.getComponentInfo(tagName);
    if (!componentInfo) {
      return;
    }
    
    const { componentClass, template } = componentInfo;
    
    // Create component instance
    const component = new componentClass();
    
    // Extract data from SSR attributes or content
    this.populateComponentFromSSR(component, element);
    
    // Queue for hydration
    this.hydrationOrchestrator.queueForHydration(
      element,
      component,
      template
    );
  }
  
  private getComponentInfo(tagName: string): {
    componentClass: any;
    template: HydratableElementViewTemplate<any>;
  } | null {
    // Component registry lookup
    const registry: Record<string, any> = {
      'hydratable-component': {
        componentClass: HydratableComponent,
        template: HydratableComponent.template
      }
    };
    
    return registry[tagName] || null;
  }
  
  private populateComponentFromSSR(component: any, element: Element): void {
    // Extract component data from SSR attributes or content
    const title = element.querySelector('h1')?.textContent;
    const description = element.querySelector('p')?.textContent;
    
    if (title) component.title = title;
    if (description) component.description = description;
  }
}

// Initialize SSR hydration
const ssrApp = new SSRApp();
document.addEventListener('DOMContentLoaded', () => {
  ssrApp.initializeWithHydration().then(() => {
    console.log('SSR hydration completed');
  }).catch(error => {
    console.error('SSR hydration failed:', error);
  });
});

Hydration Error Handling

Error handling and debugging utilities for hydration processes, helping identify and resolve hydration mismatches.

/**
 * Error thrown when hydration fails due to content mismatch
 */
class HydrationBindingError extends Error {
  /**
   * Creates a hydration binding error
   * @param message - Error message
   * @param propertyBag - Additional error information
   */
  constructor(
    message: string | undefined,
    public readonly propertyBag: {
      index: number;
      hydrationStage: string;
      itemsLength?: number;
      viewsState: string[];
      viewTemplateString?: string;
      rootNodeContent: string;
    }
  );
}

/**
 * Hydration validation utilities
 */
const HydrationValidator: {
  /**
   * Validates that server and client content match
   * @param serverContent - Content from server-side rendering
   * @param clientTemplate - Client-side template
   * @param data - Data used for rendering
   * @returns Validation result
   */
  validateContentMatch(
    serverContent: string,
    clientTemplate: ViewTemplate,
    data: any
  ): HydrationValidationResult;
  
  /**
   * Checks for common hydration issues
   * @param element - Element being hydrated
   * @returns Array of potential issues
   */
  checkForHydrationIssues(element: Element): HydrationIssue[];
  
  /**
   * Enables debug mode for hydration
   * @param enabled - Whether to enable debug mode
   */
  setDebugMode(enabled: boolean): void;
};

/**
 * Result of hydration validation
 */
interface HydrationValidationResult {
  /** Whether validation passed */
  isValid: boolean;
  
  /** List of validation errors */
  errors: HydrationValidationError[];
  
  /** List of validation warnings */
  warnings: HydrationValidationWarning[];
}

/**
 * Hydration validation error
 */
interface HydrationValidationError {
  /** Error type */
  type: 'content-mismatch' | 'structure-mismatch' | 'missing-marker';
  
  /** Error message */
  message: string;
  
  /** Expected content */
  expected: string;
  
  /** Actual content */
  actual: string;
  
  /** Location of error */
  location: {
    bindingIndex?: number;
    elementPath?: string;
  };
}

/**
 * Hydration validation warning
 */
interface HydrationValidationWarning {
  /** Warning type */
  type: 'performance' | 'accessibility' | 'seo';
  
  /** Warning message */
  message: string;
  
  /** Suggested fix */
  suggestion?: string;
}

/**
 * Potential hydration issue
 */
interface HydrationIssue {
  /** Issue severity */
  severity: 'error' | 'warning' | 'info';
  
  /** Issue category */
  category: 'content' | 'structure' | 'performance' | 'accessibility';
  
  /** Issue description */
  description: string;
  
  /** Element causing the issue */
  element: Element;
  
  /** Suggested resolution */
  resolution?: string;
}

Usage Examples:

import { 
  HydrationBindingError,
  HydrationValidator,
  HydrationValidationResult 
} from "@microsoft/fast-element";

// Hydration error handler
class HydrationErrorHandler {
  static handleHydrationError(error: HydrationBindingError): void {
    console.group('Hydration Error Details');
    console.error('Message:', error.message);
    console.log('Binding Index:', error.propertyBag.index);
    console.log('Hydration Stage:', error.propertyBag.hydrationStage);
    console.log('Views State:', error.propertyBag.viewsState);
    console.log('Root Node Content:', error.propertyBag.rootNodeContent);
    
    if (error.propertyBag.viewTemplateString) {
      console.log('Template:', error.propertyBag.viewTemplateString);
    }
    
    console.groupEnd();
    
    // Attempt recovery
    this.attemptHydrationRecovery(error);
  }
  
  private static attemptHydrationRecovery(error: HydrationBindingError): void {
    // Recovery strategies based on error stage
    switch (error.propertyBag.hydrationStage) {
      case 'content-binding':
        this.recoverContentBinding(error);
        break;
      case 'attribute-binding':
        this.recoverAttributeBinding(error);
        break;
      case 'event-binding':
        this.recoverEventBinding(error);
        break;
      default:
        console.warn('No recovery strategy available for stage:', error.propertyBag.hydrationStage);
    }
  }
  
  private static recoverContentBinding(error: HydrationBindingError): void {
    console.log('Attempting content binding recovery...');
    // Implement content binding recovery logic
  }
  
  private static recoverAttributeBinding(error: HydrationBindingError): void {
    console.log('Attempting attribute binding recovery...');
    // Implement attribute binding recovery logic
  }
  
  private static recoverEventBinding(error: HydrationBindingError): void {
    console.log('Attempting event binding recovery...');
    // Implement event binding recovery logic
  }
}

// Hydration debugging utility
class HydrationDebugger {
  private static debugMode = false;
  
  static enableDebugMode(): void {
    this.debugMode = true;
    HydrationValidator.setDebugMode(true);
    console.log('Hydration debug mode enabled');
  }
  
  static disableDebugMode(): void {
    this.debugMode = false;
    HydrationValidator.setDebugMode(false);
    console.log('Hydration debug mode disabled');
  }
  
  static validateAndLog(
    serverContent: string,
    clientTemplate: ViewTemplate,
    data: any,
    elementName: string
  ): boolean {
    if (!this.debugMode) return true;
    
    console.group(`Hydration Validation: ${elementName}`);
    
    try {
      const result = HydrationValidator.validateContentMatch(
        serverContent,
        clientTemplate,
        data
      );
      
      this.logValidationResult(result);
      
      return result.isValid;
    } catch (error) {
      console.error('Validation failed:', error);
      return false;
    } finally {
      console.groupEnd();
    }
  }
  
  private static logValidationResult(result: HydrationValidationResult): void {
    if (result.isValid) {
      console.log('✅ Hydration validation passed');
    } else {
      console.log('❌ Hydration validation failed');
    }
    
    if (result.errors.length > 0) {
      console.group('Errors:');
      result.errors.forEach((error, index) => {
        console.error(`${index + 1}. ${error.type}: ${error.message}`);
        console.log('   Expected:', error.expected);
        console.log('   Actual:', error.actual);
        
        if (error.location.bindingIndex !== undefined) {
          console.log('   Binding Index:', error.location.bindingIndex);
        }
        
        if (error.location.elementPath) {
          console.log('   Element Path:', error.location.elementPath);
        }
      });
      console.groupEnd();
    }
    
    if (result.warnings.length > 0) {
      console.group('Warnings:');
      result.warnings.forEach((warning, index) => {
        console.warn(`${index + 1}. ${warning.type}: ${warning.message}`);
        
        if (warning.suggestion) {
          console.log('   Suggestion:', warning.suggestion);
        }
      });
      console.groupEnd();
    }
  }
  
  static checkElementForIssues(element: Element): void {
    if (!this.debugMode) return;
    
    const issues = HydrationValidator.checkForHydrationIssues(element);
    
    if (issues.length === 0) {
      console.log('✅ No hydration issues found for element:', element.tagName);
      return;
    }
    
    console.group(`Hydration Issues for ${element.tagName}:`);
    
    issues.forEach((issue, index) => {
      const icon = issue.severity === 'error' ? '❌' : 
                   issue.severity === 'warning' ? '⚠️' : 'ℹ️';
      
      console.log(`${icon} ${index + 1}. [${issue.category}] ${issue.description}`);
      
      if (issue.resolution) {
        console.log(`   💡 Resolution: ${issue.resolution}`);
      }
    });
    
    console.groupEnd();
  }
}

// Production hydration with error handling
class ProductionHydrationManager {
  private errorReporter?: (error: any) => void;
  
  constructor(errorReporter?: (error: any) => void) {
    this.errorReporter = errorReporter;
  }
  
  async safeHydration<T>(
    element: Element,
    template: HydratableElementViewTemplate<T>,
    data: T
  ): Promise<boolean> {
    try {
      // Validate before hydration in development
      if (process.env.NODE_ENV === 'development') {
        HydrationDebugger.enableDebugMode();
        HydrationDebugger.checkElementForIssues(element);
      }
      
      // Perform hydration
      const view = template.hydrate(
        element.firstChild!,
        element.lastChild!,
        element
      );
      
      view.bind(data);
      
      return true;
      
    } catch (error) {
      // Handle hydration errors
      if (error instanceof HydrationBindingError) {
        HydrationErrorHandler.handleHydrationError(error);
      } else {
        console.error('Unexpected hydration error:', error);
      }
      
      // Report error in production
      this.errorReporter?.(error);
      
      // Fallback to client-side rendering
      return this.fallbackToCSR(element, template, data);
    }
  }
  
  private async fallbackToCSR<T>(
    element: Element,
    template: HydratableElementViewTemplate<T>,
    data: T
  ): Promise<boolean> {
    try {
      console.log('Falling back to client-side rendering');
      
      // Clear server-rendered content
      element.innerHTML = '';
      
      // Create fresh client-side view
      const view = template.create(element);
      view.bind(data);
      view.appendTo(element);
      
      return true;
      
    } catch (error) {
      console.error('Client-side rendering fallback failed:', error);
      this.errorReporter?.(error);
      
      return false;
    }
  }
}

// Application-level hydration setup
class AppHydration {
  private productionManager = new ProductionHydrationManager(
    (error) => {
      // Report to error tracking service
      console.error('Hydration error reported:', error);
    }
  );
  
  async hydrateApplication(): Promise<void> {
    const hydratableElements = document.querySelectorAll('[data-hydratable]');
    
    const hydrationPromises = Array.from(hydratableElements).map(element => 
      this.hydrateElement(element)
    );
    
    const results = await Promise.allSettled(hydrationPromises);
    
    // Log overall hydration results
    const successful = results.filter(r => r.status === 'fulfilled').length;
    const failed = results.filter(r => r.status === 'rejected').length;
    
    console.log(`Hydration complete: ${successful} successful, ${failed} failed`);
    
    if (failed > 0) {
      console.warn('Some components failed to hydrate and fell back to CSR');
    }
  }
  
  private async hydrateElement(element: Element): Promise<void> {
    const componentType = element.getAttribute('data-component-type');
    const componentData = this.extractComponentData(element);
    
    // Get component template (would be from registry in real app)
    const template = this.getComponentTemplate(componentType!);
    
    if (template && isHydratable(template)) {
      const success = await this.productionManager.safeHydration(
        element,
        template,
        componentData
      );
      
      if (!success) {
        throw new Error(`Failed to hydrate ${componentType}`);
      }
    }
  }
  
  private extractComponentData(element: Element): any {
    // Extract component data from element attributes or content
    const dataScript = element.querySelector('script[type="application/json"]');
    
    if (dataScript) {
      return JSON.parse(dataScript.textContent || '{}');
    }
    
    return {};
  }
  
  private getComponentTemplate(componentType: string): HydratableElementViewTemplate<any> | null {
    // Component template registry
    const templates: Record<string, any> = {
      'user-card': createHydratableTemplate(),
      'product-listing': createHydratableTemplate(),
      // Add more component templates
    };
    
    return templates[componentType] || null;
  }
}

Types

/**
 * Hydration stage type
 */
type HydrationStage = 
  | "initial"
  | "content-binding" 
  | "attribute-binding"
  | "event-binding"
  | "complete";

/**
 * Hydration marker type
 */
interface HydrationMarker {
  /** Marker type */
  type: 'content' | 'attribute' | 'element';
  
  /** Binding index */
  index: number;
  
  /** Target node ID */
  targetNodeId?: string;
  
  /** Attribute name for attribute markers */
  attributeName?: string;
}

/**
 * Hydration context interface
 */
interface HydrationContext {
  /** Current hydration stage */
  stage: HydrationStage;
  
  /** Available hydration markers */
  markers: HydrationMarker[];
  
  /** Root element being hydrated */
  rootElement: Element;
  
  /** Component data */
  data: any;
}

docs

attributes.md

context-system.md

css-styling.md

data-binding.md

dependency-injection.md

html-templates.md

index.md

observable-system.md

ssr-hydration.md

state-management.md

template-directives.md

testing-utilities.md

utilities.md

web-components.md

tile.json