Skip to main content Open Assistant Open Assistant Engagement Tools Widget Integration Best Practices Widgets, Engagement Tools, BET
Professional widget integration goes beyond basic implementation to ensure reliable, performant, and maintainable deployments. This tutorial covers advanced techniques for production-ready widget integration including:
Performance optimization - Minimize load times and resource usage
Error handling strategies - Graceful degradation and user feedback
Production deployment - Monitoring, logging, and maintenance
User experience enhancement - Loading states, responsiveness, and accessibility
Security considerations - Content Security Policy and safe integration
The practices outlined here build upon fundamental integration knowledge to deliver enterprise-grade widget implementations.
# Intended Audience
This tutorial is designed for:
Frontend developers with experience integrating Sportradar widgets
Technical leads responsible for production deployments
DevOps engineers implementing monitoring and deployment strategies
Product managers seeking to understand technical requirements for optimal widget performance
Prerequisites: Basic familiarity with widget integration, JavaScript, and web development concepts.
# Goals
By completing this tutorial, you will:
Implement robust error handling and fallback strategies
Optimize widget performance for production environments
Set up comprehensive monitoring and logging systems
Apply security best practices for safe widget integration
Create responsive, accessible widget implementations
Establish maintenance workflows for long-term reliability
# Environment Specification (Prerequisites)
To complete this tutorial, you will need:
Sportradar Widgets Account with API credentials
Development Environment with modern browser (Chrome 90+, Firefox 88+, Safari 14+)
Text Editor or IDE (VS Code, WebStorm, or similar)
Local Web Server for testing (Live Server, http-server, or similar)
Browser Developer Tools for debugging and performance analysis
# Technical Requirements
JavaScript Knowledge - ES6+, Promises, async/await
HTML/CSS Skills - DOM manipulation, responsive design
Basic Node.js (optional) - for build tools and local development
Network Access - to Sportradar APIs and CDN resources
# Optional but Recommended
Monitoring Tools - Google Analytics, LogRocket, or similar APM solution
Build Tools - Webpack, Rollup, or Vite for optimization
Testing Framework - Jest, Cypress, or Playwright for automated testing
# Part 1: Advanced Error Handling and Resilience
Professional widget integration requires comprehensive error handling that gracefully manages various failure scenarios.
First, let's create a robust widget loader with comprehensive error detection:
Create Widget Manager Class
Implement a WidgetManager class to encapsulate widget lifecycle management:
Centralized configuration for easy maintenance
Retry mechanism with exponential backoff
Timeout handling for network issues
Error state management with user feedback options
Initialize Widget Manager
Instantiate the widget manager with specific configuration:
Widget type specification using standard naming convention
Required parameters like match ID
Container binding for DOM manipulation
html
<!DOCTYPE html >
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title > Production Widget Integration </ title >
< style >
. widget-container {
position : relative ;
min-height : 200 px ;
background : # f5f5f5 ;
border-radius : 8 px ;
overflow : hidden ;
}
. widget-loading {
display : flex ;
align-items : center ;
justify-content : center ;
height : 200 px ;
color : # 666 ;
}
. widget-error {
display : flex ;
flex-direction : column ;
align-items : center ;
justify-content : center ;
height : 200 px ;
color : # d32f2f ;
text-align : center ;
padding : 20 px ;
}
. retry-button {
margin-top : 10 px ;
padding : 8 px 16 px ;
background : # 1976d2 ;
color : white ;
border : none ;
border-radius : 4 px ;
cursor : pointer ;
}
< / style >
</ head >
< body >
< div id = "scoreboard-widget" class = "widget-container" >
< div class = "widget-loading" > Loading match data... </ div >
</ div >
< script >
class WidgetManager {
constructor ( containerId , widgetConfig ) {
this . container = document . getElementById (containerId) ;
this . config = widgetConfig ;
this . retryCount = 0 ;
this . maxRetries = 3 ;
this . retryDelay = 2000 ;
this . loadTimeout = 15000 ;
}
async loadWidget () {
try {
await this . loadWidgetScript () ;
await this . initializeWidget () ;
this . onSuccess () ;
} catch (error) {
this . handleError (error) ;
}
}
}
// Initialize widget manager
const widgetManager = new WidgetManager ( 'scoreboard-widget' , {
widget : 'sr:match:scoreboard' ,
matchId : 12345678
} ) ;
< / script >
</ body >
</ html >
# Step 2: Add Comprehensive Script Loading Logic
Add API Readiness Detection
The waitForWidgetAPI method ensures the widget API is fully initialized:
Polling mechanism to detect API availability
Configurable timeout to prevent infinite waiting
Function availability check ensuring render method exists
Graceful failure with descriptive error messages
javascript
class WidgetManager {
constructor ( containerId , widgetConfig ) {
this . container = document . getElementById (containerId) ;
this . config = widgetConfig ;
this . retryCount = 0 ;
this . maxRetries = 3 ;
this . retryDelay = 2000 ;
this . loadTimeout = 15000 ;
this . scriptLoadPromise = null ;
}
async loadWidgetScript () {
if ( this . scriptLoadPromise) {
return this . scriptLoadPromise ;
}
this . scriptLoadPromise = new Promise ( ( resolve , reject ) => {
// Check if widget script is already loaded
if (window . SRWidgets) {
resolve () ;
return ;
}
const script = document . createElement ( 'script' ) ;
script . src = 'https://widgets.sir.sportradar.com/js/api/widgets.js' ;
script . async = true ;
script . crossOrigin = 'anonymous' ;
// Set up timeout
const timeout = setTimeout ( () => {
reject ( new Error ( 'Widget script load timeout' )) ;
}, this . loadTimeout) ;
script . onload = () => {
clearTimeout (timeout) ;
// Wait for widget API to be ready
this . waitForWidgetAPI () . then (resolve) . catch (reject) ;
};
script . onerror = () => {
clearTimeout (timeout) ;
reject ( new Error ( 'Widget script failed to load' )) ;
};
document . head . appendChild (script) ;
} ) ;
return this . scriptLoadPromise ;
}
async waitForWidgetAPI () {
return new Promise ( ( resolve , reject ) => {
let attempts = 0 ;
const maxAttempts = 50 ;
const checkAPI = () => {
if (window . SRWidgets && typeof window . SRWidgets . render === 'function' ) {
resolve () ;
} else if (attempts >= maxAttempts) {
reject ( new Error ( 'Widget API not available after loading' )) ;
} else {
attempts ++ ;
setTimeout (checkAPI , 100 ) ;
}
};
checkAPI () ;
} ) ;
}
}
Add Event Tracking Integration
The trackWidgetEvent method integrates with analytics platforms:
Google Analytics integration with proper event structure
Custom logging for debugging and monitoring
Flexible event handling supporting various analytics tools
Implement Comprehensive Error Handling
The handleError method provides multi-layered error management:
User feedback with friendly error messages
Retry mechanism with configurable limits
Fallback content when all retries are exhausted
Error reporting to monitoring services
javascript
class WidgetManager {
// ... previous methods
async initializeWidget () {
return new Promise ( ( resolve , reject ) => {
const initTimeout = setTimeout ( () => {
reject ( new Error ( 'Widget initialization timeout' )) ;
}, this . loadTimeout) ;
try {
const widgetConfig = {
... this . config ,
onload : () => {
clearTimeout (initTimeout) ;
console . log ( 'Widget loaded successfully' ) ;
resolve () ;
},
onerror : ( error ) => {
clearTimeout (initTimeout) ;
reject ( new Error ( `Widget initialization failed: ${ error . message || error } ` )) ;
},
ontrack : ( event ) => {
this . trackWidgetEvent (event) ;
}
};
// Clear loading state
this . container . innerHTML = '' ;
// Initialize widget
window . SRWidgets . render ( this . container , widgetConfig) ;
} catch (error) {
clearTimeout (initTimeout) ;
reject (error) ;
}
} ) ;
}
trackWidgetEvent ( event ) {
// Integration with analytics
if (window . gtag) {
gtag ( 'event' , 'widget_interaction' , {
'event_category' : 'widget' ,
'event_label' : event . type ,
'custom_parameter' : event . data
} ) ;
}
// Custom logging
console . log ( 'Widget event:' , event) ;
}
handleError ( error ) {
console . error ( 'Widget error:' , error) ;
// Show user-friendly error message
this . showErrorState (error) ;
// Attempt retry if within limits
if ( this . retryCount < this . maxRetries) {
this . scheduleRetry () ;
} else {
this . showFallbackContent () ;
}
// Report error to monitoring service
this . reportError (error) ;
}
}
# Step 4: Implement Lazy Loading and Resource Optimization
Add Resource Preloading Strategy
The preloadResources method optimizes critical resource loading:
CSS preloading for immediate styling availability
Font preloading to prevent layout shifts
Promise handling with graceful failure recovery
Duplicate prevention to avoid redundant requests
Create Skeleton Loading UI
Implement skeleton loading for better perceived performance:
Visual placeholders that match final content structure
Smooth transitions from skeleton to real content
Reduced layout shift by maintaining consistent dimensions
Professional appearance during loading states
javascript
class PerformantWidgetManager extends WidgetManager {
constructor ( containerId , widgetConfig ) {
super (containerId , widgetConfig) ;
this . intersectionObserver = null ;
this . isVisible = false ;
this . resourcesPreloaded = false ;
}
initLazyLoading () {
if ( ! ( 'IntersectionObserver' in window)) {
// Fallback for older browsers
this . loadWidget () ;
# Part 3: Production Monitoring and Maintenance
# Step 5: Implement Comprehensive Monitoring
Add Performance Metrics Collection
Track key performance indicators for widget loading:
Timing measurements using Performance API
Success rate tracking for reliability monitoring
Load time analysis for optimization opportunities
User experience metrics for business impact assessment
Integrate With Analytics and Monitoring Services
Report metrics to multiple analytics platforms:
Google Analytics integration for business metrics
Custom endpoints for internal monitoring systems
Sentry integration for error tracking and alerting
Structured logging for debugging and analysis
javascript
class MonitoredWidgetManager extends PerformantWidgetManager {
constructor ( containerId , widgetConfig ) {
super (containerId , widgetConfig) ;
this . metrics = {
loadStartTime : null ,
scriptLoadTime : null ,
widgetRenderTime : null ,
totalLoadTime : null ,
errorCount : 0 ,
retryCount : 0
};
this . setupGlobalErrorHandling () ;
}
# Part 4: Security and Accessibility Best Practices
# Step 6: Implement Security Hardening
Add Accessibility Attributes
Implement ARIA attributes for screen reader compatibility:
Role and label attributes for semantic meaning
Live regions for dynamic content updates
Busy state indicators during loading
Screen reader announcements for status changes
Create Configuration Validation
Add input validation and sanitization:
Type checking for required parameters
String sanitization removing dangerous content
Length limits preventing buffer overflow attacks
Protocol validation ensuring safe URLs
Implement Security Verification
Add runtime security checks and accessibility enhancements:
HTTPS enforcement for production environments
Context security verification checking for secure contexts
Screen reader announcements for dynamic content changes
Focus management for keyboard navigation
html
<!DOCTYPE html >
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title > Secure Widget Integration </ title >
<!-- Content Security Policy -->
< meta http-equiv = "Content-Security-Policy" content = "
default-src 'self';
script-src 'self' 'unsafe-inline' https://widgets.sir.sportradar.com;
style-src 'self' 'unsafe-inline' https://widgets.sir.sportradar.com https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https://widgets.sir.sportradar.com https://*.sportradar.com;
# Part 5: Production Deployment and Maintenance
# Step 7: Create Deployment Checklist and Monitoring
Add Continuous Health Monitoring
Implement ongoing system health checks:
Periodic monitoring with configurable intervals
Resource cleanup preventing memory leaks
Responsive detection ensuring widget remains functional
Memory usage tracking for performance optimization
Create Performance Monitoring System
Set up comprehensive performance tracking:
Real-time metrics using PerformanceObserver API
Memory usage monitoring for resource optimization
Error rate calculation for reliability assessment
Performance entry reporting for detailed analysis
Implement Deployment Reporting
Create detailed deployment status reporting:
Success/failure tracking with detailed metrics
Environment detection for context-aware monitoring
Comprehensive reporting to monitoring endpoints
Console logging for development debugging
javascript
class ProductionWidgetManager extends SecureWidgetManager {
constructor ( containerId , widgetConfig ) {
super (containerId , widgetConfig) ;
this . deploymentChecks = [] ;
this . healthCheckInterval = null ;
this . performanceObserver = null ;
this . runDeploymentChecks () ;
}
async runDeploymentChecks () {
console . log ( '🚀 Running production deployment checks...' ) ;
# Further Reading
# Official Documentation
# Security Guidelines
# Accessibility Standards
# Monitoring and Analytics
This tutorial provides production-ready patterns for professional widget integration. Always test thoroughly in staging environments before deploying to production, and monitor performance metrics continuously to ensure optimal user experience.
Last updated 2 months ago
Is this site helpful?
return ;
}
this . intersectionObserver = new IntersectionObserver (
( entries ) => {
entries . forEach ( entry => {
if (entry . isIntersecting && ! this . isVisible) {
this . isVisible = true ;
this . preloadResources () ;
setTimeout ( () => this . loadWidget () , 100 ) ;
this . intersectionObserver . disconnect () ;
}
} ) ;
},
{
rootMargin : '100px' ,
threshold : 0.1
}
) ;
this . intersectionObserver . observe ( this . container) ;
}
async preloadResources () {
if ( this . resourcesPreloaded) return ;
const resources = [
'https://widgets.sir.sportradar.com/css/api/widgets.css' ,
'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap'
] ;
const preloadPromises = resources . map ( url => {
return new Promise ( ( resolve ) => {
const link = document . createElement ( 'link' ) ;
link . rel = 'preload' ;
link . as = url . includes ( '.css' ) ? 'style' : 'script' ;
link . href = url ;
link . onload = resolve ;
link . onerror = resolve ; // Don't fail on preload errors
document . head . appendChild (link) ;
} ) ;
} ) ;
await Promise . allSettled (preloadPromises) ;
this . resourcesPreloaded = true ;
}
async loadWidget () {
// Show skeleton loading instead of basic loading
this . showSkeletonLoader () ;
try {
await super . loadWidget () ;
} catch (error) {
this . handleError (error) ;
}
}
showSkeletonLoader () {
this . container . innerHTML = `
<div class="skeleton-loader">
<div class="skeleton-header"></div>
<div class="skeleton-content">
<div class="skeleton-team">
<div class="skeleton-logo"></div>
<div class="skeleton-name"></div>
</div>
<div class="skeleton-score"></div>
<div class="skeleton-team">
<div class="skeleton-logo"></div>
<div class="skeleton-name"></div>
</div>
</div>
</div>
` ;
}
}
// Usage with performance monitoring
const performantManager = new PerformantWidgetManager ( 'scoreboard-widget' , {
widget : 'sr:match:scoreboard' ,
matchId : 12345678
} ) ;
// Start performance monitoring
performance . mark ( 'widget-init-start' ) ;
performantManager . initLazyLoading () ;
setupGlobalErrorHandling () {
// Capture unhandled widget errors
window . addEventListener ( 'error' , ( event ) => {
if (event . filename && event . filename . includes ( 'widgets.sir.sportradar.com' )) {
this . reportError ( {
type : 'script_error' ,
message : event . message ,
filename : event . filename ,
lineno : event . lineno ,
colno : event . colno
} ) ;
}
} ) ;
// Capture promise rejections
window . addEventListener ( 'unhandledrejection' , ( event ) => {
if (event . reason && event . reason . toString () . includes ( 'widget' )) {
this . reportError ( {
type : 'promise_rejection' ,
message : event . reason . toString () ,
stack : event . reason . stack
} ) ;
}
} ) ;
}
async loadWidget () {
this . metrics . loadStartTime = performance . now () ;
performance . mark ( 'widget-load-start' ) ;
try {
await super . loadWidget () ;
this . recordSuccessMetrics () ;
} catch (error) {
this . metrics . errorCount ++ ;
this . handleError (error) ;
}
}
recordSuccessMetrics () {
const endTime = performance . now () ;
this . metrics . totalLoadTime = endTime - this . metrics . loadStartTime ;
performance . mark ( 'widget-load-end' ) ;
performance . measure ( 'widget-total-load' , 'widget-load-start' , 'widget-load-end' ) ;
// Report metrics to analytics
this . reportMetrics ( {
widget_load_time : this . metrics . totalLoadTime ,
widget_type : this . config . widget ,
match_id : this . config . matchId ,
retry_count : this . retryCount ,
success : true
} ) ;
// Log to console for debugging
console . log ( 'Widget Metrics:' , this . metrics) ;
}
reportMetrics ( metrics ) {
// Google Analytics 4
if (window . gtag) {
gtag ( 'event' , 'widget_performance' , {
custom_map : { metric_1 : 'load_time' },
metric_1 : Math . round (metrics . widget_load_time)
} ) ;
}
// Custom analytics endpoint
if ( this . config . analyticsEndpoint) {
fetch ( this . config . analyticsEndpoint , {
method : 'POST' ,
headers : { 'Content-Type' : 'application/json' },
body : JSON . stringify ( {
timestamp : new Date () . toISOString () ,
page_url : window . location . href ,
user_agent : navigator . userAgent ,
... metrics
} )
} ) . catch ( err => console . warn ( 'Analytics reporting failed:' , err)) ;
}
}
reportError ( error ) {
const errorReport = {
timestamp : new Date () . toISOString () ,
page_url : window . location . href ,
user_agent : navigator . userAgent ,
widget_type : this . config . widget ,
match_id : this . config . matchId ,
error_type : error . type || 'unknown' ,
error_message : error . message ,
stack_trace : error . stack ,
retry_count : this . retryCount ,
metrics : this . metrics
};
// Log to console
console . error ( 'Widget Error Report:' , errorReport) ;
// Send to error tracking service
if (window . Sentry) {
Sentry . captureException (error , {
tags : {
component : 'widget' ,
widget_type : this . config . widget
},
extra : errorReport
} ) ;
}
// Custom error endpoint
if ( this . config . errorEndpoint) {
fetch ( this . config . errorEndpoint , {
method : 'POST' ,
headers : { 'Content-Type' : 'application/json' },
body : JSON . stringify (errorReport)
} ) . catch ( err => console . warn ( 'Error reporting failed:' , err)) ;
}
}
}
connect-src 'self' https://api.sportradar.com https://widgets.sir.sportradar.com;
frame-src 'none';
object-src 'none';
" >
<!-- Additional security headers -->
< meta http-equiv = "X-Content-Type-Options" content = "nosniff" >
< meta http-equiv = "X-Frame-Options" content = "DENY" >
< meta http-equiv = "Referrer-Policy" content = "strict-origin-when-cross-origin" >
</ head >
< body >
< main >
< h1 > Live Match Scoreboard </ h1 >
< div
id = "scoreboard-widget"
class = "widget-container"
role = "region"
aria-label = "Live match scoreboard"
aria-live = "polite"
aria-busy = "true"
>
< div class = "widget-loading" aria-label = "Loading match data" >
< span class = "sr-only" > Loading match data, please wait... </ span >
< div class = "spinner" aria-hidden = "true" ></ div >
Loading match data...
</ div >
</ div >
</ main >
< script >
class SecureWidgetManager extends MonitoredWidgetManager {
constructor ( containerId , widgetConfig ) {
super (containerId , widgetConfig) ;
this . validateConfiguration () ;
this . setupSecurityHeaders () ;
}
validateConfiguration () {
// Validate required parameters
if ( ! this . config . matchId || typeof this . config . matchId !== 'number' ) {
throw new Error ( 'Invalid or missing matchId' ) ;
}
if ( ! this . config . widget || typeof this . config . widget !== 'string' ) {
throw new Error ( 'Invalid or missing widget type' ) ;
}
// Sanitize string inputs
if ( this . config . title) {
this . config . title = this . sanitizeString ( this . config . title) ;
}
// Validate numeric inputs
if ( this . config . seasonId && ! Number . isInteger ( this . config . seasonId)) {
throw new Error ( 'Invalid seasonId - must be an integer' ) ;
}
}
sanitizeString ( input ) {
if ( typeof input !== 'string' ) return '' ;
// Remove potentially dangerous characters
return input
. replace ( / [ <> ] / g , '' ) // Remove angle brackets
. replace ( / javascript: / gi , '' ) // Remove javascript: protocol
. replace ( / on \w + = / gi , '' ) // Remove event handlers
. trim ()
. substring ( 0 , 200 ) ; // Limit length
}
setupSecurityHeaders () {
// Verify CSP is in place
const metaCSP = document . querySelector ( 'meta[http-equiv="Content-Security-Policy"]' ) ;
if ( ! metaCSP) {
console . warn ( 'Content Security Policy not found - consider adding CSP headers' ) ;
}
// Check for secure context
if ( ! window . isSecureContext) {
console . warn ( 'Insecure context detected - widgets should be served over HTTPS' ) ;
}
}
async loadWidgetScript () {
// Verify script integrity
const expectedOrigin = 'https://widgets.sir.sportradar.com' ;
if ( ! this . config . allowInsecure && ! window . location . protocol . startsWith ( 'https' )) {
throw new Error ( 'Widgets require HTTPS in production' ) ;
}
return super . loadWidgetScript () ;
}
initializeWidget () {
// Add accessibility attributes
this . container . setAttribute ( 'aria-busy' , 'false' ) ;
this . container . setAttribute ( 'aria-live' , 'polite' ) ;
return super . initializeWidget () ;
}
onSuccess () {
// Update accessibility state
this . container . setAttribute ( 'aria-busy' , 'false' ) ;
this . container . removeAttribute ( 'aria-label' ) ;
// Announce success to screen readers
this . announceToScreenReader ( 'Match data loaded successfully' ) ;
super . onSuccess () ;
}
announceToScreenReader ( message ) {
const announcement = document . createElement ( 'div' ) ;
announcement . setAttribute ( 'aria-live' , 'assertive' ) ;
announcement . setAttribute ( 'aria-atomic' , 'true' ) ;
announcement . style . position = 'absolute' ;
announcement . style . left = '-10000px' ;
announcement . style . width = '1px' ;
announcement . style . height = '1px' ;
announcement . style . overflow = 'hidden' ;
document . body . appendChild (announcement) ;
announcement . textContent = message ;
setTimeout ( () => {
document . body . removeChild (announcement) ;
}, 1000 ) ;
}
}
// Initialize secure widget manager
const secureManager = new SecureWidgetManager ( 'scoreboard-widget' , {
widget : 'sr:match:scoreboard' ,
matchId : 12345678 ,
allowInsecure : false // Enforce HTTPS in production
} ) ;
secureManager . initLazyLoading () ;
< / script >
< style >
. sr-only {
position : absolute !important ;
width : 1 px !important ;
height : 1 px !important ;
padding : 0 !important ;
margin : -1 px !important ;
overflow : hidden !important ;
clip : rect ( 0 , 0 , 0 , 0 ) !important ;
border : 0 !important ;
}
. spinner {
width : 20 px ;
height : 20 px ;
border : 2 px solid # f3f3f3 ;
border-top : 2 px solid # 1976d2 ;
border-radius : 50% ;
animation : spin 1 s linear infinite ;
display : inline-block ;
margin-right : 10 px ;
}
@keyframes spin {
0% { transform : rotate ( 0 deg ); }
100% { transform : rotate ( 360 deg ); }
}
. widget-container {
border : 1 px solid # e0e0e0 ;
border-radius : 8 px ;
padding : 16 px ;
background : white ;
box-shadow : 0 2 px 4 px rgba ( 0 , 0 , 0 , 0.1 );
}
. widget-container : focus-within {
outline : 2 px solid # 1976d2 ;
outline-offset : 2 px ;
}
< / style >
</ body >
</ html >
const checks = [
{ name : 'HTTPS' , check : () => window . location . protocol === 'https:' },
{ name : 'CSP' , check : () => document . querySelector ( 'meta[http-equiv="Content-Security-Policy"]' ) },
{ name : 'Error Handling' , check : () => typeof this . reportError === 'function' },
{ name : 'Analytics' , check : () => window . gtag || this . config . analyticsEndpoint },
{ name : 'Accessibility' , check : () => this . container . hasAttribute ( 'aria-label' ) },
{ name : 'Performance Monitoring' , check : () => 'PerformanceObserver' in window },
{ name : 'Valid Configuration' , check : () => this . validateProductionConfig () }
] ;
for ( const check of checks) {
try {
const passed = await check . check () ;
this . deploymentChecks . push ( {
name : check . name ,
status : passed ? 'PASS' : 'FAIL' ,
timestamp : new Date () . toISOString ()
} ) ;
console . log ( ` ${ passed ? '✅' : '❌' } ${ check . name } ` ) ;
} catch (error) {
this . deploymentChecks . push ( {
name : check . name ,
status : 'ERROR' ,
error : error . message ,
timestamp : new Date () . toISOString ()
} ) ;
console . log ( `❌ ${ check . name } : ${ error . message } ` ) ;
}
}
this . reportDeploymentStatus () ;
}
validateProductionConfig () {
const requiredFields = [ 'widget' , 'matchId' ] ;
const missingFields = requiredFields . filter ( field => ! this . config[field]) ;
if (missingFields . length > 0 ) {
throw new Error ( `Missing required fields: ${ missingFields . join ( ', ' ) } ` ) ;
}
// Check for development-only settings
if ( this . config . debug && window . location . hostname !== 'localhost' ) {
console . warn ( 'Debug mode enabled in production environment' ) ;
}
return true ;
}
async loadWidget () {
// Start health monitoring
this . startHealthMonitoring () ;
// Initialize performance monitoring
this . initPerformanceMonitoring () ;
try {
await super . loadWidget () ;
this . reportDeploymentSuccess () ;
} catch (error) {
this . reportDeploymentFailure (error) ;
throw error ;
}
}
startHealthMonitoring () {
// Periodic health checks
this . healthCheckInterval = setInterval ( () => {
this . performHealthCheck () ;
}, 60000 ) ; // Check every minute
// Clean up on page unload
window . addEventListener ( 'beforeunload' , () => {
if ( this . healthCheckInterval) {
clearInterval ( this . healthCheckInterval) ;
}
if ( this . performanceObserver) {
this . performanceObserver . disconnect () ;
}
} ) ;
}
performHealthCheck () {
const healthMetrics = {
timestamp : new Date () . toISOString () ,
widget_responsive : this . isWidgetResponsive () ,
memory_usage : this . getMemoryUsage () ,
error_rate : this . calculateErrorRate () ,
load_time : this . metrics . totalLoadTime
};
// Report to monitoring service
if ( this . config . healthCheckEndpoint) {
fetch ( this . config . healthCheckEndpoint , {
method : 'POST' ,
headers : { 'Content-Type' : 'application/json' },
body : JSON . stringify (healthMetrics)
} ) . catch ( err => console . warn ( 'Health check reporting failed:' , err)) ;
}
// Log locally for debugging
console . log ( 'Health Check:' , healthMetrics) ;
}
isWidgetResponsive () {
try {
// Check if widget container has content
const hasContent = this . container . children . length > 0 ;
// Check if widget is visible
const rect = this . container . getBoundingClientRect () ;
const isVisible = rect . width > 0 && rect . height > 0 ;
return hasContent && isVisible ;
} catch (error) {
return false ;
}
}
getMemoryUsage () {
if ( 'memory' in performance) {
return {
used : Math . round (performance . memory . usedJSHeapSize / 1048576 ) , // MB
total : Math . round (performance . memory . totalJSHeapSize / 1048576 ) , // MB
limit : Math . round (performance . memory . jsHeapSizeLimit / 1048576 ) // MB
};
}
return null ;
}
calculateErrorRate () {
const totalAttempts = this . retryCount + 1 ;
return totalAttempts > 0 ? ( this . metrics . errorCount / totalAttempts) * 100 : 0 ;
}
initPerformanceMonitoring () {
if ( 'PerformanceObserver' in window) {
this . performanceObserver = new PerformanceObserver ( ( list ) => {
for ( const entry of list . getEntries ()) {
if (entry . name . includes ( 'widget' )) {
this . reportPerformanceEntry (entry) ;
}
}
} ) ;
this . performanceObserver . observe ( { entryTypes : [ 'measure' , 'navigation' , 'resource' ] } ) ;
}
}
reportPerformanceEntry ( entry ) {
const performanceData = {
name : entry . name ,
duration : entry . duration ,
startTime : entry . startTime ,
entryType : entry . entryType ,
timestamp : new Date () . toISOString ()
};
console . log ( 'Performance Entry:' , performanceData) ;
// Report to analytics
this . reportMetrics (performanceData) ;
}
reportDeploymentStatus () {
const report = {
timestamp : new Date () . toISOString () ,
page_url : window . location . href ,
user_agent : navigator . userAgent ,
checks : this . deploymentChecks ,
overall_status : this . deploymentChecks . every ( check => check . status === 'PASS' ) ? 'HEALTHY' : 'ISSUES_DETECTED'
};
console . log ( '📊 Deployment Status Report:' , report) ;
if ( this . config . deploymentReportEndpoint) {
fetch ( this . config . deploymentReportEndpoint , {
method : 'POST' ,
headers : { 'Content-Type' : 'application/json' },
body : JSON . stringify (report)
} ) . catch ( err => console . warn ( 'Deployment reporting failed:' , err)) ;
}
}
reportDeploymentSuccess () {
performance . mark ( 'widget-deployment-success' ) ;
console . log ( '🎉 Widget deployment successful' ) ;
this . reportMetrics ( {
deployment_status : 'success' ,
deployment_time : performance . now () ,
environment : this . getEnvironment ()
} ) ;
}
reportDeploymentFailure ( error ) {
performance . mark ( 'widget-deployment-failure' ) ;
console . error ( '💥 Widget deployment failed:' , error) ;
this . reportError ( {
type : 'deployment_failure' ,
message : error . message ,
stack : error . stack ,
environment : this . getEnvironment () ,
deployment_checks : this . deploymentChecks
} ) ;
}
getEnvironment () {
const hostname = window . location . hostname ;
if (hostname === 'localhost' || hostname === '127.0.0.1' ) return 'development' ;
if (hostname . includes ( 'staging' ) || hostname . includes ( 'test' )) return 'staging' ;
return 'production' ;
}
}
// Production deployment with comprehensive configuration
const productionManager = new ProductionWidgetManager ( 'scoreboard-widget' , {
widget : 'sr:match:scoreboard' ,
matchId : 12345678 ,
// Analytics endpoints
analyticsEndpoint : 'https://analytics.yoursite.com/api/events' ,
errorEndpoint : 'https://monitoring.yoursite.com/api/errors' ,
healthCheckEndpoint : 'https://monitoring.yoursite.com/api/health' ,
deploymentReportEndpoint : 'https://monitoring.yoursite.com/api/deployment' ,
// Security settings
allowInsecure : false ,
// Performance settings
maxRetries : 3 ,
loadTimeout : 15000 ,
// Feature flags
debug : false ,
enableHealthChecks : true ,
enablePerformanceMonitoring : true
} ) ;
// Initialize with full monitoring
productionManager . initLazyLoading () ;