First Init
Deploy to Production / Build & Verify (push) Failing after 5m56s
Ping Search Engines / Notify Search Engines (push) Successful in 2s
Deploy to Production / Pre-Deploy Tests (push) Has been skipped
Deploy to Production / Deploy to Railway (push) Has been skipped
Deploy to Production / Deploy to Render (push) Has been skipped
Deploy to Production / Deploy to VPS (PM2) (push) Has been skipped
Deploy to Production / Deploy to Fly.io (push) Has been skipped
Deploy to Production / Post-Deploy Verification (push) Has been skipped
Deploy to Production / Notify on Failure (push) Successful in 2s
E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
E2E Test Suite / Smoke Tests (P0) (push) Failing after 11m26s
E2E Test Suite / Form Interaction Tests (push) Failing after 11m42s
E2E Test Suite / Destructive & Chaos Tests (push) Failing after 12m2s
E2E Test Suite / Cross-Browser Regression (chromium) (push) Failing after 16m14s
E2E Test Suite / Cross-Browser Regression (webkit) (push) Failing after 17m45s
E2E Test Suite / Cross-Browser Regression (firefox) (push) Failing after 25m23s
E2E Test Suite / Security Header Tests (push) Failing after 7m55s
E2E Test Suite / Test Report Summary (push) Failing after 20s
E2E Test Suite / Mobile Device Tests (push) Failing after 2h49m9s
Uptime Monitor / Health & Response Time (push) Failing after 2s
Uptime Monitor / SSL Certificate (push) Successful in 2s
Uptime Monitor / Send Alerts (push) Failing after 3s
Uptime Monitor / Record Uptime Success (push) Has been skipped

This commit is contained in:
2026-03-21 16:46:46 +05:30
commit d402256547
216 changed files with 48375 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
/**
* Analytics event tracking utility
*
* Supports both Google Analytics 4 (gtag) and Plausible.
* Safe to call even when analytics is not configured — silently no-ops.
*
* Usage:
* import { trackEvent } from '../utils/analytics';
* trackEvent('form_submit', { form_name: 'contact' });
*/
interface EventParams {
[key: string]: string | number | boolean;
}
/**
* Track a custom event in GA4 and/or Plausible
*/
export function trackEvent(eventName: string, params: EventParams = {}): void {
// Guard: skip if DNT is enabled
if (navigator.doNotTrack === '1' || (window as any).doNotTrack === '1') {
return;
}
// Google Analytics 4
if (typeof (window as any).gtag === 'function') {
(window as any).gtag('event', eventName, params);
}
// Plausible Analytics
if (typeof (window as any).plausible === 'function') {
(window as any).plausible(eventName, { props: params });
}
}
/**
* Track external link clicks
* Call this on anchor elements pointing to external domains.
*/
export function trackExternalLink(url: string, label?: string): void {
trackEvent('external_link_click', {
link_url: url,
link_label: label ?? url,
outbound: true,
});
}
/**
* Track form submission events
*/
export function trackFormSubmit(formName: string, success: boolean): void {
trackEvent(success ? 'form_submit_success' : 'form_submit_error', {
form_name: formName,
});
}
/**
* Track newsletter signup events
*/
export function trackNewsletterSignup(success: boolean): void {
trackEvent(success ? 'newsletter_signup_success' : 'newsletter_signup_error', {
form_name: 'newsletter',
});
}
/**
* Track portfolio filter clicks
*/
export function trackPortfolioFilter(filter: string): void {
trackEvent('portfolio_filter', {
filter_category: filter,
});
}
/**
* Track portfolio case study views
*/
export function trackCaseStudyView(projectId: string, projectTitle: string): void {
trackEvent('case_study_view', {
project_id: projectId,
project_title: projectTitle,
});
}
/**
* Auto-bind external link tracking to all external anchor tags on the page.
* Call once after DOM is ready.
*/
export function bindExternalLinkTracking(): void {
const currentHost = window.location.hostname;
document.querySelectorAll<HTMLAnchorElement>('a[href]').forEach((anchor) => {
try {
const url = new URL(anchor.href, window.location.href);
const isExternal = url.hostname !== currentHost && url.hostname !== '';
if (isExternal) {
anchor.addEventListener('click', () => {
trackExternalLink(url.href, anchor.textContent?.trim() || anchor.getAttribute('aria-label') || url.href);
});
}
} catch {
// Invalid URL, skip
}
});
}
+133
View File
@@ -0,0 +1,133 @@
/**
* Scroll-reveal & micro-interaction utilities
* Uses Intersection Observer — no external libraries required.
*/
/**
* Initialise scroll-reveal for all [data-animate] elements.
* Call once per page after DOM is ready.
*/
export function initScrollReveal(): void {
// Skip entirely when the user prefers reduced motion
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
document.querySelectorAll<HTMLElement>('[data-animate]').forEach((el) => {
el.classList.add('is-visible');
});
return;
}
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('is-visible');
// Unobserve after triggering — animate once
observer.unobserve(entry.target);
}
});
},
{
threshold: 0.12,
rootMargin: '0px 0px -40px 0px',
}
);
document.querySelectorAll('[data-animate]').forEach((el) => observer.observe(el));
}
/**
* Animates a numeric counter from 0 to `target`.
* Attaches to elements with [data-counter] attribute.
*/
export function initCounters(): void {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
const counterObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
const el = entry.target as HTMLElement;
const target = parseInt(el.dataset.counter ?? '0', 10);
const duration = parseInt(el.dataset.counterDuration ?? '2000', 10);
const suffix = el.dataset.counterSuffix ?? '';
animateCounter(el, target, duration, suffix);
counterObserver.unobserve(el);
});
},
{ threshold: 0.5 }
);
document.querySelectorAll('[data-counter]').forEach((el) =>
counterObserver.observe(el)
);
}
function animateCounter(
el: HTMLElement,
target: number,
duration: number,
suffix: string
): void {
const start = performance.now();
const step = (now: number) => {
const elapsed = now - start;
const progress = Math.min(elapsed / duration, 1);
// Ease-out cubic
const eased = 1 - Math.pow(1 - progress, 3);
const current = Math.floor(eased * target);
el.textContent = current.toLocaleString() + suffix;
el.classList.toggle('counting', progress < 1);
if (progress < 1) requestAnimationFrame(step);
};
requestAnimationFrame(step);
}
/**
* Adds a radial highlight that follows the cursor on `.btn-ripple` elements.
*/
export function initButtonRipple(): void {
document.querySelectorAll<HTMLElement>('.btn-ripple').forEach((btn) => {
btn.addEventListener('mousemove', (e: MouseEvent) => {
const rect = btn.getBoundingClientRect();
const x = ((e.clientX - rect.left) / rect.width) * 100;
const y = ((e.clientY - rect.top) / rect.height) * 100;
btn.style.setProperty('--x', `${x}%`);
btn.style.setProperty('--y', `${y}%`);
});
});
}
/**
* Fills `.progress-bar` elements to their `--progress-width` CSS var
* once they scroll into view.
*/
export function initProgressBars(): void {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
document.querySelectorAll<HTMLElement>('.progress-bar').forEach((el) => {
el.classList.add('is-visible');
});
return;
}
const pbObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('is-visible');
pbObserver.unobserve(entry.target);
}
});
},
{ threshold: 0.4 }
);
document.querySelectorAll('.progress-bar').forEach((el) => pbObserver.observe(el));
}
/** Convenience: run all animation initialisers in one call. */
export function initAllAnimations(): void {
initScrollReveal();
initCounters();
initButtonRipple();
initProgressBars();
}
+67
View File
@@ -0,0 +1,67 @@
/**
* Image Optimization Utilities
*
* Helper functions for optimizing external image URLs
* and generating responsive image srcsets.
*/
/**
* Optimize Unsplash URL with WebP format and quality settings
*/
export function optimizeUnsplashUrl(
url: string,
options: {
width?: number;
height?: number;
quality?: number;
format?: 'webp' | 'auto';
} = {}
): string {
if (!url.includes('unsplash.com')) return url;
const { width, height, quality = 80, format = 'webp' } = options;
// Parse existing URL
const urlObj = new URL(url);
// Set optimization parameters
if (width) urlObj.searchParams.set('w', width.toString());
if (height) urlObj.searchParams.set('h', height.toString());
urlObj.searchParams.set('q', quality.toString());
urlObj.searchParams.set('fm', format);
urlObj.searchParams.set('fit', 'crop');
urlObj.searchParams.set('auto', 'format,compress');
return urlObj.toString();
}
/**
* Generate srcset for responsive images
*/
export function generateSrcset(
url: string,
widths: number[] = [320, 640, 960, 1280],
aspectRatio?: number
): string {
if (!url.includes('unsplash.com')) return '';
return widths
.map((w) => {
const height = aspectRatio ? Math.round(w / aspectRatio) : undefined;
const optimizedUrl = optimizeUnsplashUrl(url, { width: w, height });
return `${optimizedUrl} ${w}w`;
})
.join(', ');
}
/**
* Default image sizes attribute for responsive images
*/
export const defaultSizes = '(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw';
/**
* Generate blur placeholder data URL (10x10 pixel)
*/
export function getBlurPlaceholder(color: string = '#1e293b'): string {
return `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Crect fill='${encodeURIComponent(color)}' width='10' height='10'/%3E%3C/svg%3E`;
}
+115
View File
@@ -0,0 +1,115 @@
/**
* Structured Logger
*
* Provides consistent, structured logging across the application.
* - In development: pretty-prints to console with colors
* - In production: outputs JSON for log aggregation (Logtail, Datadog, etc.)
*
* Usage:
* import { logger } from '@/utils/logger';
* logger.info('api.contact', 'Form submitted', { email: '...' });
* logger.error('api.contact', 'Email failed', { error: err.message });
*/
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
interface LogEntry {
level: LogLevel;
scope: string;
message: string;
timestamp: string;
[key: string]: unknown;
}
const LEVELS: Record<LogLevel, number> = {
debug: 0,
info: 1,
warn: 2,
error: 3,
};
// Minimum log level — override via LOG_LEVEL env var
function getMinLevel(): LogLevel {
const env = (typeof process !== 'undefined' && process.env.LOG_LEVEL) ||
(typeof import.meta !== 'undefined' && (import.meta as { env?: { LOG_LEVEL?: string } }).env?.LOG_LEVEL);
if (env && env in LEVELS) return env as LogLevel;
return (typeof import.meta !== 'undefined' && (import.meta as { env?: { PROD?: boolean } }).env?.PROD)
? 'info'
: 'debug';
}
function shouldLog(level: LogLevel): boolean {
return LEVELS[level] >= LEVELS[getMinLevel()];
}
function formatEntry(level: LogLevel, scope: string, message: string, meta?: Record<string, unknown>): LogEntry {
return {
level,
scope,
message,
timestamp: new Date().toISOString(),
...meta,
};
}
const isProd = typeof import.meta !== 'undefined'
? (import.meta as { env?: { PROD?: boolean } }).env?.PROD === true
: process.env.NODE_ENV === 'production';
const COLORS: Record<LogLevel, string> = {
debug: '\x1b[90m', // gray
info: '\x1b[36m', // cyan
warn: '\x1b[33m', // yellow
error: '\x1b[31m', // red
};
const RESET = '\x1b[0m';
function output(entry: LogEntry): void {
if (isProd) {
// JSON output for log aggregation in production
// eslint-disable-next-line no-console
console.log(JSON.stringify(entry));
} else {
const color = COLORS[entry.level] ?? '';
const { level, scope, message, timestamp, ...rest } = entry;
const meta = Object.keys(rest).length > 0 ? ' ' + JSON.stringify(rest) : '';
// eslint-disable-next-line no-console
console.log(`${color}[${level.toUpperCase()}]${RESET} ${timestamp} [${scope}] ${message}${meta}`);
}
}
function log(level: LogLevel, scope: string, message: string, meta?: Record<string, unknown>): void {
if (!shouldLog(level)) return;
output(formatEntry(level, scope, message, meta));
}
export const logger = {
debug: (scope: string, message: string, meta?: Record<string, unknown>) => log('debug', scope, message, meta),
info: (scope: string, message: string, meta?: Record<string, unknown>) => log('info', scope, message, meta),
warn: (scope: string, message: string, meta?: Record<string, unknown>) => log('warn', scope, message, meta),
error: (scope: string, message: string, meta?: Record<string, unknown>) => log('error', scope, message, meta),
};
/**
* Log an API request/response pair. Use at the end of API handlers.
*/
export function logApiRequest(opts: {
scope: string;
method: string;
path: string;
status: number;
ip?: string;
durationMs?: number;
meta?: Record<string, unknown>;
}): void {
const { scope, method, path, status, ip, durationMs, meta } = opts;
const level: LogLevel = status >= 500 ? 'error' : status >= 400 ? 'warn' : 'info';
log(level, scope, `${method} ${path} ${status}`, {
method,
path,
status,
...(ip && { ip }),
...(durationMs !== undefined && { durationMs }),
...meta,
});
}
+218
View File
@@ -0,0 +1,218 @@
/**
* Sentry Error Tracking — Optional Integration
*
* Activated only when SENTRY_DSN environment variable is set.
* Uses the Sentry REST API directly (no SDK dependency required).
*
* For full SDK integration, install @sentry/node and replace captureException
* with the SDK's init() + Sentry.captureException().
*
* Usage:
* import { captureException, captureMessage } from '@/utils/sentry';
* captureException(error, { scope: 'api.contact', extra: { ip } });
* captureMessage('Rate limit hit', 'warning', { scope: 'api.contact' });
*/
import { logger } from './logger';
interface SentryContext {
scope?: string; // e.g. 'api.contact'
user?: { ip?: string; email?: string };
extra?: Record<string, unknown>;
tags?: Record<string, string>;
}
interface SentryEnvelope {
event_id: string;
timestamp: string;
platform: string;
level: string;
environment: string;
release?: string;
transaction?: string;
logger?: string;
exception?: {
values: Array<{
type: string;
value: string;
stacktrace?: { frames: Array<{ filename?: string; function?: string; lineno?: number }> };
}>;
};
message?: string;
user?: { ip_address?: string; email?: string };
extra?: Record<string, unknown>;
tags?: Record<string, string>;
}
function getSentryDsn(): string | undefined {
try {
return (import.meta as { env?: { SENTRY_DSN?: string } }).env?.SENTRY_DSN;
} catch {
return process.env.SENTRY_DSN;
}
}
function getEnvironment(): string {
try {
return (import.meta as { env?: { PROD?: boolean } }).env?.PROD ? 'production' : 'development';
} catch {
return process.env.NODE_ENV ?? 'development';
}
}
function getRelease(): string | undefined {
try {
return process.env.npm_package_version ?? process.env.RELEASE_VERSION;
} catch {
return undefined;
}
}
function generateEventId(): string {
return Array.from({ length: 32 }, () => Math.floor(Math.random() * 16).toString(16)).join('');
}
/** Parse DSN into project URL and auth header */
function parseDsn(dsn: string): { url: string; auth: string } | null {
try {
const url = new URL(dsn);
const key = url.username;
const projectId = url.pathname.replace('/', '');
const storeUrl = `${url.protocol}//${url.host}/api/${projectId}/store/`;
return { url: storeUrl, auth: `Sentry sentry_version=7, sentry_key=${key}` };
} catch {
return null;
}
}
async function sendToSentry(payload: SentryEnvelope): Promise<void> {
const dsn = getSentryDsn();
if (!dsn) return;
const parsed = parseDsn(dsn);
if (!parsed) {
logger.warn('sentry', 'Invalid SENTRY_DSN format — skipping event');
return;
}
try {
const res = await fetch(parsed.url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Sentry-Auth': parsed.auth,
},
body: JSON.stringify(payload),
});
if (!res.ok) {
logger.warn('sentry', 'Failed to send event to Sentry', { status: res.status });
}
} catch (err) {
// Never let Sentry reporting crash the application
logger.warn('sentry', 'Network error sending to Sentry', {
error: err instanceof Error ? err.message : String(err),
});
}
}
/**
* Capture an exception and send to Sentry (if configured).
* Also logs the error via the structured logger.
*/
export async function captureException(
error: unknown,
context: SentryContext = {}
): Promise<void> {
const err = error instanceof Error ? error : new Error(String(error));
const scope = context.scope ?? 'app';
// Always log locally
logger.error(scope, err.message, {
error: err.name,
stack: err.stack?.split('\n').slice(0, 5).join(' | '),
...context.extra,
});
// Send to Sentry if DSN is configured
const dsn = getSentryDsn();
if (!dsn) return;
const frames = err.stack
?.split('\n')
.slice(1)
.map((line) => {
const match = line.trim().match(/at\s+(.+?)\s+\((.+?):(\d+):\d+\)/) ??
line.trim().match(/at\s+(.+?):(\d+):\d+/);
if (match) {
return {
function: match[1],
filename: match[2] ?? match[1],
lineno: parseInt(match[3] ?? match[2], 10),
};
}
return { filename: line.trim() };
}) ?? [];
await sendToSentry({
event_id: generateEventId(),
timestamp: new Date().toISOString(),
platform: 'node',
level: 'error',
environment: getEnvironment(),
release: getRelease(),
logger: scope,
transaction: scope,
exception: {
values: [{
type: err.name,
value: err.message,
stacktrace: { frames },
}],
},
user: context.user
? { ip_address: context.user.ip, email: context.user.email }
: undefined,
extra: context.extra,
tags: context.tags,
});
}
/**
* Capture a message (non-exception) and send to Sentry (if configured).
* Also logs the message via the structured logger.
*/
export async function captureMessage(
message: string,
level: 'info' | 'warning' | 'error' = 'info',
context: SentryContext = {}
): Promise<void> {
const scope = context.scope ?? 'app';
if (level === 'error') {
logger.error(scope, message, context.extra);
} else if (level === 'warning') {
logger.warn(scope, message, context.extra);
} else {
logger.info(scope, message, context.extra);
}
const dsn = getSentryDsn();
if (!dsn) return;
await sendToSentry({
event_id: generateEventId(),
timestamp: new Date().toISOString(),
platform: 'node',
level: level === 'warning' ? 'warning' : level,
environment: getEnvironment(),
release: getRelease(),
logger: scope,
message,
user: context.user
? { ip_address: context.user.ip, email: context.user.email }
: undefined,
extra: context.extra,
tags: context.tags,
});
}
+223
View File
@@ -0,0 +1,223 @@
// SEO Utility Functions
// Helpers for generating meta tags, structured data, and canonical URLs
export interface SEOMetadata {
title: string;
description: string;
keywords?: string[];
canonicalUrl?: string;
ogImage?: string;
ogType?: 'website' | 'article' | 'profile';
twitterCard?: 'summary' | 'summary_large_image' | 'player';
publishedTime?: string;
modifiedTime?: string;
author?: string;
noIndex?: boolean;
}
/**
* Generate page title with site name
*/
export function generatePageTitle(title: string, siteName: string = 'WorkRoot IT Solutions'): string {
if (title === 'Home' || title.includes(siteName)) {
return siteName;
}
return `${title} | ${siteName}`;
}
/**
* Generate keywords from tags and categories
*/
export function generateKeywords(tags: string[], additionalKeywords: string[] = []): string {
const baseKeywords = [
'web development',
'mobile apps',
'AI solutions',
'cloud services',
'IT consulting',
];
const allKeywords = [...new Set([...baseKeywords, ...tags, ...additionalKeywords])];
return allKeywords.join(', ');
}
/**
* Generate breadcrumb structured data
*/
export function generateBreadcrumbSchema(breadcrumbs: Array<{ name: string; url: string }>, siteUrl: string = 'https://workroot.in') {
return {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: breadcrumbs.map((item, index) => ({
'@type': 'ListItem',
position: index + 1,
name: item.name,
item: `${siteUrl}${item.url}`,
})),
};
}
/**
* Generate Article structured data for blog posts
*/
export function generateArticleSchema(
title: string,
description: string,
author: string,
publishedTime: string,
modifiedTime: string,
imageUrl: string,
url: string,
keywords: string[] = []
) {
return {
'@context': 'https://schema.org',
'@type': 'Article',
headline: title,
description: description,
image: imageUrl,
datePublished: publishedTime,
dateModified: modifiedTime,
author: {
'@type': 'Person',
name: author,
},
publisher: {
'@type': 'Organization',
name: 'WorkRoot IT Solutions',
logo: {
'@type': 'ImageObject',
url: 'https://workroot.in/logo.png',
},
},
mainEntityOfPage: {
'@type': 'WebPage',
'@id': url,
},
keywords: keywords.join(', '),
};
}
/**
* Generate FAQ structured data
*/
export function generateFAQSchema(faqs: Array<{ question: string; answer: string }>) {
return {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: faqs.map((faq) => ({
'@type': 'Question',
name: faq.question,
acceptedAnswer: {
'@type': 'Answer',
text: faq.answer,
},
})),
};
}
/**
* Generate Service structured data
*/
export function generateServiceSchema(
serviceName: string,
description: string,
features: string[] = [],
siteUrl: string = 'https://workroot.in'
) {
return {
'@context': 'https://schema.org',
'@type': 'Service',
name: serviceName,
description: description,
provider: {
'@type': 'Organization',
name: 'WorkRoot IT Solutions',
url: siteUrl,
},
areaServed: {
'@type': 'GeoShape',
name: 'Worldwide',
},
serviceType: serviceName,
offers: {
'@type': 'Offer',
availability: 'https://schema.org/InStock',
},
hasOfferCatalog: {
'@type': 'OfferCatalog',
name: `${serviceName} Features`,
itemListElement: features.map((feature, index) => ({
'@type': 'Offer',
itemOffered: {
'@type': 'Service',
name: feature,
},
})),
},
};
}
/**
* Generate canonical URL
*/
export function generateCanonicalUrl(pathname: string, siteUrl: string = 'https://workroot.in'): string {
// Remove trailing slash for consistency (except for root)
const cleanPath = pathname === '/' ? pathname : pathname.replace(/\/$/, '');
return `${siteUrl}${cleanPath}`;
}
/**
* Truncate description to optimal length
*/
export function truncateDescription(description: string, maxLength: number = 160): string {
if (description.length <= maxLength) {
return description;
}
// Find last complete word before maxLength
const truncated = description.substring(0, maxLength);
const lastSpace = truncated.lastIndexOf(' ');
return truncated.substring(0, lastSpace) + '...';
}
/**
* Generate Open Graph image URL
*/
export function generateOGImageUrl(imagePath: string, siteUrl: string = 'https://workroot.in'): string {
if (imagePath.startsWith('http://') || imagePath.startsWith('https://')) {
return imagePath;
}
return `${siteUrl}${imagePath.startsWith('/') ? imagePath : `/${imagePath}`}`;
}
/**
* Check if URL should be indexed
*/
export function shouldIndexPage(pathname: string): boolean {
const noIndexPatterns = [
'/admin',
'/private',
'/api',
'/preview',
'/draft',
];
return !noIndexPatterns.some(pattern => pathname.startsWith(pattern));
}
/**
* Generate social media share URLs
*/
export function generateShareUrls(url: string, title: string) {
const encodedUrl = encodeURIComponent(url);
const encodedTitle = encodeURIComponent(title);
return {
twitter: `https://twitter.com/intent/tweet?url=${encodedUrl}&text=${encodedTitle}`,
facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`,
linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,
email: `mailto:?subject=${encodedTitle}&body=${encodedUrl}`,
};
}